Spring底层源码解析
Bean的生命周期
- 通过构造方法创建一个普通bean,进行实例化(如果只有一个构造方法无论是有参还是无参都会直接使用,如果有多个构造方法优先执行指定的(@Autoweried修饰的构造方法),如果没有指定会去寻找无参构造方法如果没有找到则报错)
- 遍历它的属性,给对象里面那些加了Autowired注解的属性去赋值
- 处理aware回调(set属性)
- 初始化前,处理@PostConstruct注解
- 初始化,执行afterPropertiesSet()方法(针对实现了Initializing接口的bean),在这个方法里面我们可以自己写一些逻辑,像是对某个属性进行验证啊,赋值之类的,因为已经给属性赋值过了可以对数据库进行一些操作,或者开启定时任务。
- 初始化后,如果需要aop,得到代理对象,如果不需要得到的就是实例化的对象
@Autowired和@Resource
@Autowired:先byType去获取对象,后byName
@Resource:byName
AOP如何实现的?和AspectJ有什么区别?
AOP就是面向切面编程,一个类里面有一些方法,不用修改这个方法本身,现在需要在执行这个方法之前或者执行这个方法之后执行一些逻辑,像一些公共的逻辑我们可以把他提取出来像是日志啊权限校验啊这些逻辑,然后会生成一个代理对象,如果一个bean实现了接口就会采用jdk动态代理,如果没有实现一个接口会采用cglib动态代理,让代理对象去执行这些逻辑。
- aspectJ是在编译期去增强的,并且设计了一些注解@Before @After @Around
- AOP是通过动态代理去实现的
通过IOC实现策略模式
if-else很多的情况下可以用策略模式优化
优化前:
优化后:
Spring事务是如何实现的
- 基于aop实现的
- 对使用了@Transactional注解的bean,创建一个代理对象
- 调用一个方法的时候会先判断他有没有加@Transactional注解
- 如果加了会创建一个数据库连接,修改数据库连接autocommit为false,禁止自动提交
- 之后执行这个方法,执行了一些sql之后不会提交而是继续执行直到执行完整个方法之后看是否抛异常,如果没有抛异常就提交,如果抛了异常调用回滚方法,可以指定异常@Transactional(rollback=xxx),指定到什么异常它才去回滚。
- 使用@Transactional注解时在类上必须加@Configuration否则不生效,因为如果不加@Configuration jdbc和spring都会去建立数据库连接,并且会建立两个不同的连接对象,jdbc建立的连接autocommit默认为true不会回滚,而@Configuration可以保证两种方式建立的是同一个连接。
- 在方法上使用@Transactional,如果该方法内还调用了该类中的方法事务会失效,因为事务的实现本质锁aop,它生成代理对象只会完成数据库连接等操作,因为代理对象中的属性并没有进行依赖注入所以属性都为空,执行后面的逻辑是普通对象执行的,普通对象执行的逻辑事务锁不生效的,解决方式可以是在该类中自己注入自己,然后就是代理对象去调用方法事务就可以生效了。
Bean实例化和Bean初始化有什么区别
Bean实例化:执行类的构造方法从而获取一个java对象
Bean初始化:执行afterPropertiesSet()方法,在这个方法里面我们可以自己写一些逻辑,像是对某个属性进行验证啊,赋值之类的
Bean是否是线程安全的
涉及到共享变量的单例bean是不安全的,可以加锁、原子类、作用域改为多例来保证线程安全。
Spring事务的传播机制
- 一个线程在执行的过程中,开启了一个spring事务,在执行接下来的一些方法时会去判断是共用一个事务还是开启新的事务,这就是传播机制,我们可以根据不同的业务场景去进行配置
- 默认是用required,就是用同一个事务
- requries_new,另外开启一个事务
Spring事务失效
- 没加@Configuration注解
- 方法是私有
- 新开启一个线程,这个线程和之前的线程不是一个事务,这两个线程是独立的相互不影响的
- 方法内的自调用:Spring事务他是基于aop的,一个方法里面他去调用另一个方法的时候只有代理对象去调用这个方法的时候事务才会生效,在一个方法里面调this.xxx的时候因为这个this不是代理对象它只是一个普通对象所以事务会失效,解决方式是自己注入自己,因为这个spring在创造bean的时候在初始化结束之后它会去判断这个对象需不需要aop,如果需要的话它会生成一个代理对象,并且把它放到一个map里面,下次还要获取这个对象可以直接从map里面拿,然后通过自己注入的那个对象去调方法就可以了
ApplicationContext和BeanFactory
- BeanFactory就是bean工厂,它可以创建一个bean
- ApplicationContext是BeanFactory的扩展,它继承了一些别的接口c
SpringBoot启动过程中做了哪些事情
- 判断当前的应用类型
- 创建Spring容器,启动容器
- 解析启动类,扫描,将项目中的自动配置类导入到spring容器中去
- 启动tomcat
- 调用ApplicationRunner
Spring容器启动的流程是怎样的
- 首先会扫描,扫描到所有的beanDefinition对象,存放在一个map里
- 创建bean:通过构造方法去创建一个对象对这个对象进行实例化,然后依赖注入给属性去赋值,处理aware回调,在初始化之前处理postConstruct注解,初始化的时候调用afterPropertiesSet方法,里面可以写一些逻辑像是参数的校验赋值之类的,在初始化之后查看一下这个对象是否需要aop,如果需要生成一个代理对象,如果不需要就用那个普通的对象
- bean创建完成之后,Spring会发布一个容器启动事件
- Spring启动结束
SpringBootApplication注解有什么用?
- 这个注解是一个复合注解相当于SpringBootConfiguration、EnableAutoConfiguration、ComponentSan这三个注解
- SpringBootConfiguration:代表这个类是一个配置类
- ComponentSan:扫描被@Component (@Service,@Controller)注解的 bean
- EnableAutoConfiguration:将项目中的自动配置类导入到spring容器中去
SpringBoot中的spring.factories文件有什么用
- 对SpringBoot进行一些扩展,像文件中去增加一些配置类,在这个文件里去增加就可以了就不需要写什么代码
SpringMVC处理请求的流程
- 浏览器发送请求给dispatcharServlet
- dispatcharServlet会调用handlerMapping根据uri找到能适配处理的handler
- dispatcharServlet会调用HandlerAdapter适配器执行 Handler
- Handler完成处理之后会返回给dispatcharServlet一个modelAndView
- 之后modelAndView经过视图渲染,把结果返回给浏览器
springboot自动装配
- 自动装配就是我们在引入第三方的依赖的时候不用再像以前spring写xml去配置了,只用在pom里面引入一个依赖在配置文件里面简单的配置一下就可以引入这个依赖,之后直接去依赖注入就可以了。
- 这里就要说到springBoot的一个核心注解@SpringBootApplication,这个注解是一个复合注解他可以拆分为三个注解分别是configuration,EnableAutoConfiguration、componentScan,其中自动装配只要是靠EnableAutoConfiguration这个注解
- 这个注解其实是通过AutoConfigurationImportSelector这个类去实现的
- AutoConfigurationImportSelector底层去实现了ImportSelector接口,里面有一个方法叫selectImports方法,这个方法会获取META-INF/spring.factories中所有符合条件的全限定类名,这些类会被加载到IOC容器中。
private static final String[] NO_IMPORTS = new String[0];
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// <1>.判断自动装配开关是否打开
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//<2>.获取所有需要装配的bean
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
- spring.factories中这么多配置,每次启动都要全部加载么?
@ConditionalOnXXX可以根据条件筛选需要加载的类
Spring循环依赖
-
产生的原因:
Spring内部会用三级缓存来处理循环依赖 -
singletonObject 一级缓存 用来存储创建完成的对象
-
earlySingletonObject 二级缓存 在创建单例bean时,如果发现该bean存在循环依赖,spring会进行提前aop创建代理对象并将代理对象放入二级缓存,因为这些对象还没有进行属性赋值所以不能直接放入单例池,当循环依赖的bean创建完成之后,会对二级缓存中的对象进行赋值,存入单例池中。
-
singletonFactories 三级缓存 存储的是单例bean的创建工厂
-
bean创建流程
- A 调用doCreateBean()创建Bean对象:由于还未创建,从第1级缓存singletonObjects查不到,此时只是一个半成品(提前暴露的对象),放入第3级缓存singletonFactories(存入的是一个创建bean的lambda表达式)。
- A在属性填充时发现自己需要B对象,但是在三级缓存中均未发现B,于是创建B的半成品,放入第3级缓存singletonFactories。
- B在属性填充时发现自己需要A对象,从第1级缓存singletonObjects和第2级缓存earlySingletonObjects中未发现A,但是在第3级缓存singletonFactories中发现A,将A放入第2级缓存earlySingletonObjects,同时从第3级缓存singletonFactories删除。
将A注入到对象B中。 - B完成属性填充,执行初始化方法,将自己放入第1级缓存singletonObjects中(此时B是一个完整的对象),同时从第3级缓存singletonFactories和第2级缓存earlySingletonObjects中删除。
- A得到“对象B的完整实例”,将B注入到A中。
- A完成属性填充,执行初始化方法,并放入到第1级缓存singletonObjects中。
在创建过程中,都是从第三级缓存(对象工厂创建不完整对象),将提前暴露的对象放入到第二级缓存;从第二级缓存拿到后,完成初始化,并放入第一级缓存。
- @Lazy解决循环依赖
如果属性已经初始化完成注入完成了,但是又生成代理对象,相当于注入的这个对象被改变了,spring不允许这种情况发生就会报错。解决方式就是使用@Lazy
就是给被@Lazy修饰的对象在依赖注入的时候先生成代理对象,延迟对象的创建,就相当于这两个对象就不是互相注入了。