一、循环依赖(循环引用)的概念
循环依赖其实就是循环引用,也就是一个或多个Bean互相持有对方,最终形成闭环。比如:A依赖于B,B依赖与A。
@Component
public class A{
@Autowired
private B b;
}
@Component
public class B{
@Autowired
private A a;
}
以下都属于循环引用
二、三级缓存解决循环依赖
循环依赖会带来死循环的问题。为了解决这个问题,Spring中引入了三级缓存。对应的三级缓存如下所示:
//单实例对象注册器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry{
private static final int SUPPERSSED_EXCEPTIONS_LIMIT=100;
//一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
//二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
//三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
| 缓存名称 | 源码名称 | 作用 |
|---|---|---|
| 一级缓存 | singletonObjects | 单例池,缓存已经经历了完整的生命周期并且已经初始化完成的Bean对象。限制Bean在BeanFactory中只存一份,即实现singleton scope,解决不了循环依赖。 |
| 二级缓存 | earlySingletonObjects | 缓存早期的Bean对象(生命周期还没有走完)。 |
| 三级缓存 | singletonFactories | 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的。可以创建代理对象,也可以创建普通对象。 |
2.1 二级缓存
如果要想打破循环依赖,就需要一个中间人的参与,这个人就是二级缓存。
如果只是普通对象间的相互依赖,只用二级缓存就可以。但是如果涉及到代理对象,必须要引入三级缓存。
2.2 三级缓存
三级缓存解决的是Bean对象初始化过程中的循环依赖问题。
三级缓存解决了Spring框架大部分的循环依赖问题。但是仍然还有不能解决的循环依赖问题,比如在构造方法注入产生了循环依赖。
2.3 构造方法出现循环依赖
如下面代码所示,A对象与B对象都在各自的构造方法中注入对方。
@Component
public class A{
//成员变量B
private B b;
A(B b){
this.b = b;
}
}
@Component
public class B{
//成员变量A
private A a;
B(A a){
this.a = a;
}
}
三级缓存可以解决初始化过程中的循环依赖,不能解决构造函数产生的循环依赖。
解决方法:使用@Lazy注解,延迟加载注入对象。
@Component
public class A{
//成员变量B
private B b;
A(@Lazy B b){
this.b = b;
}
}