什么是循环依赖
互相引用,形成一个环。
依赖方式
Bean的创建
根据直接互相依赖展示流程
要解决循环依赖,就得解决第二次A创建过程
Spring 如何解决的?
依赖的三级缓存
// 一级
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 三级
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 二级
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
示例代码
@Component
public class A {
@Autowired
private B b;
}
@Component
public class B {
@Autowired
private A a;
}
@ComponentScan(basePackages = {"com.wl.anination"})
public class Config {
}
Spring 代码梳理
具体创建 bean 的过程此处不再讲解,请参考 文章 Spring 源码之 Bean 的创建。
- 创建A
-
Object sharedInstance = getSingleton(beanName); // 从容器获取 【AbstractBeanFactory.doGetBean】
获取为空:
beforeSigletonCreation,记录当前bean正在创建
markBeanAsCreated,放置于alreadyCreated
-
doCreateBean创建A
判断正在创建中:加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
-
populateBean 设置属性
发现需要设置属性B,从容器重获取,首次没有,此时创建B,到 2
-
获取到B 设置属性值
- 创建B,流程同A
Object sharedInstance = getSingleton(beanName); // 从容器获取 【AbstractBeanFactory.doGetBean】
获取为空:
beforeSigletonCreation,记录当前bean正在创建
markBeanAsCreated,放置于alreadyCreated
doCreateBean创建A
判断正在创建中:加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
populateBean 设置属性
发现需要设置属性A,从容器获取
getSingleton:从一级取,从二级取,最后从三级取,三级取到时放到二级缓存并删除三级缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 一级缓存取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 一级缓存没有且正在创建,从二级取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 二级也没有,加锁
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 从一级取
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 从二级取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 三级取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 获取对象,删除三级缓存,放至二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
-
addSigleton
放至一级缓存、删除二级缓存和三级缓存
-
此时B创建完成,完整的B对象,存于一级缓存,返回到A创建设置B属性
到此看到循环解决办法,二级缓存可以解决吗?我试着改了下代码,当前示例是没有问题的
代码改动:设计2处改动
getSigleton
addSingletonFactory:调用此处时改成只放二级缓存
可以正常输出,说明二级缓存是可以解决循环依赖的
我们添加 C 类,此类添加aop拦截,就会报错
为什么会有这样的错?
A 创建时,设置B 属性,B创建,此时引用A(前期创建),二级缓存放B 前期引用,B 被动态代理,此时B有两个状态;
初始化在前,动态代理在后,可能会改变对象,所以在属性设置时,获取三级缓存,通过lambda直接生成代理对象。
两级缓存可以解决普通依赖,对于代理的对象就不可以了。
三级缓存是何时放进去的?
一级缓存:完全创建后,调用 addSigleton ,删除二、三级缓存
二级缓存:需要获取bean,调用getSingleton,删除三级缓存,对象放于二级缓存
三级缓存:实例化bean,调用addSingletonFactory,放于三级缓存
来自公众号:一个程序猿的笔记