1. 什么是循坏依赖
很简单,其实就是互相依赖对方,比如,有一个A对象依赖了B对象,B对象又依赖了A对象。
// A依赖了B
public class A{
private B b;
}
// B依赖了A
public class B{
private A a;
}
但是,在我们普通的java开发中,循坏依赖会出现问题吗?
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖注入是很正常的事情。
比如:
这样,A,B就互相依赖上了。
但是,在Spring中循环依赖就是一个问题了,为什么?
因为,在Spring中,一个对象并不是简单new出来了,而是会经过一系列的Spring Bean的生命周期,正是因为Bean的生命周期所以才会出现循环依赖问题。当然,在Spring中,出现循环依赖的场景很多,有的场景Spring自动帮我们解决了,而有的场景则需要程序员来解决,下面具体分析。
要明白Spring中的循环依赖,得先弄明白Spring中Bean的生命周期。
2. 前置知识
2.1 Spring bean 生命周期
这里不会对Bean的生命周期进行详细的分析,只描述一下大概的过程。
Bean的生命周期指的就是:在Spring启动中,Bean是如何生成的?
被Spring管理的对象叫做Bean。Bean的生成步骤如下:
class --> BeanDefinition --> 实例化(前后)--> 属性填充 --> 初始化(前后) --> bean 加入单例池
- Spring扫描class得到每个beanName对应的BeanDefinition;
- 根据得到的BeanDefinition去生成bean;
- 首先根据class推断构造方法;
- 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象/普通对象);
- 填充原始对象中的属性(依赖注入);
- 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象;
- 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例池拿即可。
可以发现,在Spring中,构造一个Bean,包括了new这个步骤(第4步构造方法反射-->实例化)。
通过反射构造了一个原始对象之后,就需要进行属性填充(依赖注入),注入的过程大致如下:
上面的A类需要注入一个B类的b属性,则当A实例化之后,就需要去给b属性赋值。在属性填充之前,会先去获得属性值,但是此时B对象还没有被实例化,所以需要先去实例化B才可以。此时递归的去构建B对象。同理,在创建B类的Bean的过程中,B类中也存在一个A类的a属性,在B实例化之后,进行属性填充,又去获取A对象(但是A正在创建中),从而形成循环依赖:
ABean创建-->依赖了B属性-->触发BBean创建--->B依赖了A属性--->需要ABean(但ABean还在创建过程中)
从而导致ABean创建不出来,BBean也创建不出来。
因此,下面我们具体分析,在Spring中,它帮我们解决了哪些场景的循环依赖。
2.2 主要用到4个集合缓存
-
Map<String, Object> singletonObjects (一级缓存)
key:beanName,value:经历了spring完整生命周期的bean对象。
-
Map<String, Object> earlySingletonObjects(二级缓存)
key:beanName,value:不完整的单例对象,也就是还没有初始化完毕。
-
Map<String, ObjectFactory<?>> singletonFactories(三级缓存)
key:beanName,value:单例工厂,用于创建beanName所对应的Bean对象。
-
Set< String > singletonsCurrentlyInCreation
set集合,存放正在创建的Bean的名称,创建完成后,从这个集合中删除。
3. 调试与分析
3.1 调试
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
@Configuration
@ComponentScan("com.example.demo.test")
public class AppConfig {
}
public class test {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
}
}
熟悉Spring Bean生命周期源码的读者,应该都知道,我们可以直接跟进在AbstractApplicationContext#refresh()中的代码:
// 完成所有剩余的非懒加载的单例bean的实例化与初始化过程
finishBeanFactoryInitialization(beanFactory);
继续跟进到 beanFactory.preInstantiateSingletons()
如上,遍历所有的beanNames,判断当前创建的bean是否是FactoryBean子类,这里A是一个普通的bean(非FactoryBean子类),所以直