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已经被代理,且被循环依赖,就会直接报错

举报
评论 0