什么是循环依赖?
@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,有就直接使用缓存中的,不会再去创建