spring的Bean是单例的吗?
spring的Bean默认是单例的,可以通过修改@Scope的参数改成多例,但在开发中很少会用用到多例模式。
spring的单例Bean是线程安全吗?
spring框架中的Bean不是线程安全的,spring的Bean默认情况下是单例模式,业务一搬的BEan都是注入无状态的对象,没有线程安全问题,但如果Bean中定义了可修改的成员变量,就要考虑线程安全问题了,解决问题是枷锁或者使用多例来解决
spring AOP
什么是AOP呢?
AOP是一种面向切面的变成,他的核心思想是将那些与业务无关的,却对多个对象产生影响的公共行为和逻辑,比如日志,事务,权限等抽取出来疯转成一个可重用的切面。
AOP的实现原理是动态代理,spring在运行时为目标对象生成一个代理对象,在调用目标方法时,代理对象会执行前面里的通知,然后在执行真正的业务代码。
在我的项目中呢我在记录操作日志中使用了AOP,我的实现流程是这样的,我写了一个@OperationLog注解,里面actionType(),actionDesc(),detail()这三个记录操作的详细信息在定义一个切面类使用@AfterReturning这个方法执行完在执行的通知记录操作日志,当然因为这个操作日志是使用了微服务架构,所以这个操作日志是采用了openfeign远程调用实现了写入user_log这张表
AOP的常见常见还要做缓存,虽然在我的项目中没有实现,但我在做一些demo时会使用,实现思路是,先写一个@interface注解,接收要拦截方法的公共key,然后使用方法执行前执行的通知@Around来查缓存,查不到就使用joinPoint.proceed()方法让业务代码执行并获取写入缓存的数据在AOP中实现缓存的写入。
spring中的事务是如何实现的?
在spring的项目中我们经常使用的是声明式事务管理,声明式管理的事务实现逻辑是使用AOP实现的,他的逻辑是字方法执行前进行拦截,然后开启事务,使用joinPoint.proceed()让业务代码执行,当业务逻辑执行完后会提交事务当报错时就会回滚事务。
事务失效的场景
情况一:异常捕获处理
事务触发的条件是事务捕捉到了跑出来的异常,才会进行回滚,当事务已经被自己处理掉了,事务通知无法知道故而会出现事务失效,解决方法是在catch写一个throw new RuntimeException把异常抛出去
情况二:抛出检查类异常
什么是检查类异常?检查类异常就是,不会强制开发者解决的异常,比如FileNotFoundException这个找不到文件异常,spring的事务是不会回滚的,解决方法就是在@Transactional上加上这类异常的捕获
情况三:非pubulic方法加上@Transactional就会导致事务失效,解决方法就是换成pubulic
Bean的生命周期?!!!!!
Bean的三级缓存?!!!!!
spring MVC的执行流程
JSP时代的执行流程:
1.用户发送请求到前端控制器,前端控制器又被称为调度中心
2.前端控制器收到请求会调用处理器映射器,处理器映射器会使用map来存储我们在后端写的接口,key是路由,value是方法名,处理器映射器找到对应的处理器会生成一个处理器执行链,处理器执行链有可能会包含拦截器,处理器映射器会将处理器执行链返回给前端控制器
3.前端控制器调用处理器适配器来执行方法,处理器适配器调用具体的处理器来执行,执行完处理器适配器对返回结果进行封装返回给前端控制器,
4.前端控制器将执行的结果传给视图解析器解析器,视图解析器解析后返回view对象给前端控制器
5.前端控制器渲染数据展示
前后端分离的流程:
1.用户发送请求到前端控制器,前端控制器又被称为调度中心
2.前端控制器收到请求会调用处理器映射器,处理器映射器会使用map来存储我们在后端写的接口,key是路由,value是方法名,处理器映射器找到对应的处理器会生成一个处理器执行链,处理器执行链有可能会包含拦截器,处理器映射器会将处理器执行链返回给前端控制器
3.前端控制器调用处理器适配器来执行方法,处理器适配器调用具体的处理器来执行,执行完后由于Controller会有@RestController会把数据封装成JOSN格式的数据。
4.数据会通过HttpMessageConverter返回JOSN数据给前端渲染展示
spring boot的自动配置
springboot项目中都会有@SpringBootApplication,这个注解呢包含了三个注解@SprinBootConfiguration,@ComponentScan,@EnableAutoConfiguration;其中呢@EnableAutoConfiguration是自动话配置的核心注解,@EnableAutoConfiguration的底层工作流程如下:
1.该注解内部会使用@Import导入AutoConfigurationImportSelector类这个选择器去读取配置文件
2.这个配置文件的路径在jar包的META---INF/spring.factories,选择器会读取这个文件中的配置的类的全类名。
3.这些配置类中定义了很多Bean,但springBoot不会全都加载,而是先判断该当前的classpath有没有对应的字节码文件,有就会把这个配置类定义的Bean导入到Spring容器中。
mybatis执行流程
SpringBoot工程在启动时会自动加载yaml文件有关于mybatis的配置
首先我们通过@Autwired注入Mapper接口时我们会获取到一个动态代理对象,当我们调用userMapper.selectById(1)时代理对象会拦截下来。
第二代理对象会通过sqlsessionTemplate获取一个线程安全的sqlsession
第三拿到sqlssion后会根据全限定名(接口路径+方法名)去全局配置中查找对应的MappedStatement,这个对象封装了这条sql需要的所有信息,包括sql语句,参数映射等
第四Executor执行器接管。sqlsession把任务交给Executor执行器,执行器会检查一级二级缓存命中则返回,没命中则继续往下走
第五执行sql。执行器会调用StatementHandler,StatemenHandler会创建JDBC的,执行sql拿到ResulSet,然后把数据映射成Java对象返回,最后销毁
mybatis的延迟加载
什么是延迟加载呢?延迟加载就是按需要去查询数据。比如查询user时并不想把订单列表查出来,而是等真正需要了在查,减少不必要的浪费,提高性能
怎么配置呢?可以在mybatis的配置中的把这个开关打开,这个打开是全局配置
延迟加载的底层原理:CGLIB的动态代理
查询时(如查询User)mybatis会返回一个代理的User对象,这个对象的关联属性是null,当代码调用user.getOrder的方法时CGLIB代理对象会使用拦截器拦截下来,发现用户的订单列表为null时会立即配置好子查询把订单列表查出来set进去
mybatis的一级缓存和二级缓存
一级缓存:一级缓存的的作用域是SqlSession,SqlSession的声明周期:当没有开启事务时,方法调用及结束,有事务时会等事务提交或回滚才会结束,当SQLsession的生命周期结束就会直接清空数据,当然当数据进行了增删改操作也会清空数据。一级缓存的是基于PerpetualCache的HashMap的本地缓存实现的,当我们在Sqlsession的生命周期结束之前执行两次查询相同的数据就会走一级缓存
二级缓存:二级缓存的作用域是Namespace和Mapper级别 namespace是一个mapper的接口,Mapper是一个接口类。二级缓存的底层也是PerpetualCache的HashMap基于本地缓存实现的,二级缓存默认是关闭的,开启需要在mybatis的配置文件中加<setting name="cacheEnabled" value="true"/>,xml文件中加<cache/> 。二级缓存也是在数据进行增删改时会清理数据。
数据的查询流程是先查一级缓存在查二级缓存。二级缓存也有一个痛点,因为是按照Mapper分的作用域,当多个Mapper增删改一张(如联合操作和多个Mapper操作一张表)的时候,数据是不会清空的从而出现脏读,所有生产环境一般不推荐使用二级缓存,一般都是使用Redis来做缓存。