在Spring中,一个对象并不是简单 new出来了,而是会经过一系列的Bean的生命周期。
1、Bean的生命周期
被Spring管理的对象叫做Bean。Bean的生命周期指的就是:在Spring中,Bean是如何生成的?
Bean的生成步骤如下:
- Spring扫描class得到BeanDefinition
- 根据得到的BeanDefinition去生成bean
- 首先根据class推断构造方法
- 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象)
- 填充原始对象中的属性(依赖注入)
- 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象
- 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接 从单例池拿即可
2、什么是循环依赖?
很简单,就是A对象依赖了B对象,B对象依赖了A对象
比如:
// A依赖了B
class A{
public B b;
}
// B依赖了A
class B{
public A a;
}
那么在创建B类的Bean的过程中,如果B类中存在一个A类的a属性,那么在创建B的Bean的过程中就 需要A类对应的Bean,但是,触发B类Bean的创建的条件是A类Bean在创建过程中的依赖注入,所以这里就出现了循环依赖:
ABean创建–>依赖了B属性–>触发BBean创建—>B依赖了A属性—>需要ABean(但ABean还在 创建过程中)
从而导致ABean创建不出来,BBean也创建不出来
3、Spring通过使用三级缓存机制来解决循环依赖问题
- 一级缓存(singletonObjects):已完全初始化的单例bean缓存。
- 二级缓存(earlySingletonObjects):提前暴露的bean缓存,保存早期暴露但未完全初始化的单例bean。
- 三级缓存(singletonFactories):保存可以创建早期单例bean的工厂。
如果某个bean出现了循环依赖, 就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把
代理对象
放入earlySingletonObjects中,否则就是把原始对象放入 earlySingletonObjects,为了给其他对象使用。
4、Spring处理循环依赖的简要过程
- 当一个bean被创建时,首先会检查一级缓存(singletonObjects)中是否已有该bean。
- 如果一级缓存中没有,则会检查二级缓存(earlySingletonObjects)。
- 如果二级缓存中也没有,则会检查三级缓存(singletonFactories)。
- 如果三级缓存中存在bean的创建工厂,则通过工厂创建该bean的早期引用,并将其放入二级缓存中。
- 然后,Spring会继续完成bean的初始化,并在初始化完成后,将其放入一级缓存,并从二级和三级缓存中移除。
通过这种方式,Spring能够在bean尚未完全初始化时提供其早期引用,从而解决循环依赖问题。
5、注意事项
尽管Spring能够通过三级缓存机制解决大多数循环依赖问题,但对于构造器注入的情况,Spring无法解决。这是因为构造器注入要求在bean完全创建之前就要解决依赖,而这时三级缓存机制尚未介入,连普通对象都没办法获取到。因此,避免在构造器注入中形成循环依赖非常重要。
可以通过以下方式避免构造器注入的循环依赖:
- 使用setter注入或字段注入代替构造器注入。
- 重新设计bean之间的依赖关系,减少或消除循环依赖。
- 使用@Lazy注解延迟加载bean。
@Lazy注解会直接生成代理对象,使用时其实还是使用的真正bean对象
Spring容器的启动流程可参看Spring容器的启动流程详解