Spring容器初始化和解决循环依赖问题的分析
1. Spring IOC容器初始化过程:
2. bean的生命周期
Bean 容器找到配置文件中 Spring Bean 的定义。(beanDefintion)
Bean 容器利用 Java Reflection API 创建一个Bean的实例。(执行构造方法)
如果涉及到一些属性值 利用 set()方法设置一些属性值。(set属性)
如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。(aware接口的相关方法)
如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入
ClassLoader对象的实例。
与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor
对象,执行postProcessBeforeInitialization() 方法 (BeanPostProcessor的before方法)
如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。(afterPropertiesSet方法)
如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。(init-method方法)
如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor
对象,执行postProcessAfterInitialization() 方法 (BeanPostProcessor的after方法(生成代理类))
当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。(destroy方法)
当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。(destroy-method方法)
3. 什么是Spring的循环依赖?
一种是A依赖了B,B又依赖了A,
另一种情况是A自己依赖了自己。
无线套娃
4. Spring是如何解决循环依赖的?
Spring运用了三级缓存解决了循环依赖
singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
B中从三级缓存取出的A早期引用和A本身是一个引用,这样当A完成了初始化后,B中的A也完成了初始化
4.1 普通bean(未经代理的bean)解决循环依赖
如果单纯地解决普通bean循环依赖,A在赋值前,把早期bean放入缓存,然后B在赋值A时,从缓存中取出。其实用一层缓存就够了,为什么需要做3层缓存呢?
4.2 事务代理类解决循环依赖
上面说为什么需要做3层缓存,其中一个原因就是为了解决代理类的循环依赖。如果我们只用一层缓存,那么B依赖的就是一个普通的bean,而不是一个代理bean。
而我们的第三级缓存中调用的工厂方法
getEarlyBeanReference,会调用事务的后置处理器AbstractAutoProxyCreator的getEarlyBeanReference生成代理
AbstractAutoProxyCreator生成代理的方法做了处理,只会被代理一次,这样就解决了一个bean被多次代理(生成多个代理类)的问题
如果A被循环依赖且被事务代理,根据上面的逻辑,B中的A是代理类,而A自己因为循环依赖中的B先一步生成了A的代理,所以A不会在initializeBean中生成代理类。这样会导致两个bean不相等,所以spring在bean初始化的最后,会进行校验,如果A没有被代理并且缓存中有A,这样会把缓存中的A赋值A自己,这样就解决了两个A不一致的问题。
4.3 没有循环依赖的bean怎么生成代理类
没有循环依赖的话,initializeBean方法中会调用beanPostProcessor的after方法,生成所有的代理(包括事务)
4.4 其他的代理循环依赖报错
比如A加了@async,A->b->A就会循环依赖报错,
原因是bean在初始化最后校验中,如果发现A已经被代理,且被循环依赖,就会直接报错
请先 后发表评论~