什么是循环依赖?
@Component
public class CircleA {
@Autowired
private CircleB B;
}
@Component
public class CircleB {
@Autowired
private CircleA A;
}
可以看出CircleA 了CircleB ,CircleB 注入了CircleA,循环依赖就是循环引用,就是两个或多个bean相互之间的持有对方
循环依赖什么时候可以解决?
出现循环依赖的Bean必须要是单例
这个很好理解,就是类需要是单例的
依赖注入的方式不能全是构造器注入的方式
@Component
public class A {
// @Autowired
// private B b;
public A(B b) {
}
}
@Component
public class B {
// @Autowired
// private A a;
public B(A a){
}
}
以上代码中,B是A构造方法的参数,A是B构造方法的参数,这种依赖注入的方式全是构造器注入的方式
启动的话会报错
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
依赖情况 | 依赖注入方式 | 循环依赖是否被解决 |
---|---|---|
AB相互依赖(循环依赖 ) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法 ,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法 ,A中注入B的方式为构造器 | 否 |
Spring是如何解决的循环依赖?
先说总结,Spring通过三级缓存的方式解决循环依赖,假设A ref B,B ref A。用户第一次尝试getBean(A),发现没有这个bean就去初始化,初始化的时候一开始就把这个A的实例放进容器,然后才开始setProperties,这就会触发B的实例的初始化。创建B的bean的时候赋值属性的时候发现需要一个A的bean,可以直接从容器中获取,虽然A的bean只是一个空架子没有完成初始化,但是已经可以引用了
源码分析
还是以上述的A B为例,创建一个bean就是分下面两种情况,如果这个bean在缓存中没有,就新建,如果三级缓存中有,就去三级缓存中去拿
- 创建一个新的Bean
- 从缓存中获取到已经被创建的对象
是不是跟redis经典策略有点像?缓存中有就拿缓存的,如果没有就去数据库拿,然后刷新缓存。
接下来看一下源码
// 创建bean会调用getSingleton方法
public Object getSingleton(String beanName) {
return getSingleton(beanName, true); // getSingleton会去三级缓存中获取bean
}
整个缓存一共有三级
- singletonObjects,一级缓存,存储的是所有创建好了的单例Bean
- earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象
- singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
如果A是第一次创建,那么这三个缓存中都没有A
如果都没有就会调用另外一个重载方法getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// 这里加了synchronized 锁
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 在单例对象创建前先做一个标记
// 将beanName放入到singletonsCurrentlyInCreation这个集合中
// 标志着这个单例Bean正在创建
// 如果同一个单例Bean多次被创建,这里会抛出异常
beforeSingletonCreation(beanName);// 创建bean之前
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// ...
// 省略catch异常处理
// ...
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到一级缓存singletonObjects中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
从下面的代码就可以分析出,一级缓存中放的是完全创建好了的单例Bean
if (newSingleton) {
// 添加到一级缓存singletonObjects中
addSingleton(beanName, singletonObject);
}
因为B需要注入A,所以在创建B的时候,又会去调用getBean(a),这个时候就又回到之前的流程了,但是不同的是,之前的getBean是为了创建Bean,而此时再调用getBean不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories中,所以此时getBean(a)的流程就是这样子了
问题
之前的问题就是A创建完毕之后,属性注入会创建B,B创建完毕之后又会创建A,然后A又创建B这样无限循环
解决
综上,A创建时没有a对象,就会去创建a对象,然后放入三级缓存,然后B创建的时候会创建A,创建A时会先去缓存中查询是否有A,有就直接使用缓存中的,不会再去创建