1. 前言
这是网上比较火的一个八股文,现在springboot高的版本都直接禁用了循环依赖。所以我个人觉得循环依赖本身就是设计上的一种缺陷,如果出现了循环依赖,得看看自己的设计是否合理。那么为什么要聊这个话题呢?其实spring解决循环依赖的思想还是值得借鉴。关于这个问题,网上其实很多文章都没有讲到根本,也都只是八股而已。
2. 自己如何解决循环依赖?
@Component
public class ClassA {
@Autowired
private ClassB classB;
}
@Component
public class ClassB {
@Autowired
private ClassA classA;
}
如何设计代码去解决循环依赖?
如果只有一个容器,很难解决循环依赖。
因为容器的概念,就是装状态确定的东西。所以如果只有一个容器,那么这个容器里面装的一定是完成初始化后的bean。
一个bean的生命周期是:
- 实例化
- 属性赋值
- 初始化
- 销毁
如果 ClassA 和 classB 发生了循环依赖。
只使用一个容器
public static void main(String[] args) {
// 创建 A
ClassA a = new ClassA();
// 进入 a 的属性赋值阶段
// 找 a 的属性 b 的实例
Object b = singletonObjects.get("b");
if (b == null) {
// 创建对象b
b = new ClassB();
// 进入 b 的属性赋值阶段
// 寻找 b 的属性 a 的示例
Object aObj = singletonObjects.get("a");
// 找不到 a 的属性实例 程序无法执行下去....
}
}
我们可以看到如果只有一个容器(这个容器专注保存完成初始化的bean),那么显然不能完成
那么我们可以加一个容器,用来保存完成实例化,但是没有完成属性赋值 + 初始化的bean
/**
* 保存完成初始化的bean
*/
final static Map<String, Object> singletonObjects = new HashMap<>();
/**
* 保存完成实例化,但是没有完成属性赋值 + 初始化的bean
*/
final static Map<String, Object> earlySingletonObjects = new HashMap<>();
public static void main(String[] args) {
// 创建 A
ClassA a = new ClassA();
// 将 A 放入 earlyFactories
earlySingletonObjects.put("a", a);
// 进入 A 的属性赋值阶段
// 找 A 的属性 b 的实例
Object bObj = singletonObjects.get("b");
if (bObj == null) {
// 创建对象 B
ClassB b = new ClassB();
// 将 B 放入
earlySingletonObjects.put("b", b);
// 进入 B 的属性赋值阶段
// 寻找 B 的属性 A 的实例
ClassA aObj = ((ClassA) earlySingletonObjects.get("a"));
// 属性赋值
b.setA(aObj);
// b 进行初始化....
// b 移除 earlyFactories 放入 singletonObjects
earlySingletonObjects.remove("b");
singletonObjects.put("b", b);
}
// A 进行初始化...
// A 移除 earlyFactories 放入 singletonObjects
earlySingletonObjects.remove("a");
singletonObjects.put("a", a);
ClassB b = (ClassB) singletonObjects.get("b");
System.out.println(b.getA() == a);
}
总结:
解决循环依赖需要借助于状态不同的容器
3. spring里面解决循环依赖的思路
直接看图吧:
4. 思考
为什么spring用了三个容器?
- singletonObjects 存放成品bean
- earlySingletonObjects 存放实例化后的bean,但是没有完成属性赋值 + 初始化
- singletonFactories 存放的是一个对象工厂,可以用来创建bean
我们知道,每个容器代表的是一个种状态。
现在来考虑一种场景,A 需要 AOP 动态代理,A依赖B,B依赖A。
过程:
实例化A
A塞一个对象工厂到 singletonFactories
A属性赋值
需要获得属性B
实例化B
B赛一个对象工厂到 singletonFactories
B属性赋值
注意,到了B属性赋值阶段,B 会从 singletonFactories里面获得 A 的 对象工厂,然后对象工厂里面是封装了一段逻辑,这段逻辑是判断 A 是否需要AOP动态代理,如果需要AOP动态代理,那么这里通过对象工厂获得的A就是经过代理的A,这样做的目的是为了 这种场景下 B 拿到的是 A 的动态代理对象,而不是原始对象!。做完这些之后,B会把A塞到 earlySingletonObjects.
B初始化
B从三级缓存中把自己移除,加入到 singletonObjects;
A 完成属性赋值阶段
A 完成初始化阶段
注意,A在返回对象之前,会检查自己是否已经被AOP代理了,如果是,那么这里会返回代理对象。
假设现在是 B 要设置 属性 A 的值,所以会调用 getSingleton("a", ture)
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) {
// 这里会通过 工厂对象 获得 A 的代理对象
singletonObject = singletonFactory.getObject();
// 加入到二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从三级缓存中删除
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
对象a在初始化的时候,会检查自己是否已经创建了代理对象,如果是,那么直接用代理对象。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.markAsPostProcessed();
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
throw bce;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
// 注意这里
if (earlySingletonExposure) {
// 这里会检查自身已经发生了AOP动态代理,这里实际上是从 二级缓存 earlySingletonObjects 里面取出来的,如果是,那么直接使用代理对象
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
所以使用三个缓存的目的就是:三个缓存是三个不同的状态。
- 一级缓存:成品bean
- 二级缓存:完成了实例化,但是没有完成属性赋值 + 初始化的bean,但是在二级缓存的bean一定确定了是否需要AOP
- 三级缓存,这里面放的不是bean,而是一段逻辑,这段逻辑里面可以判断某个对象是否需要AOP动态代理,如果需要,为其生成代理对象。
总结
为什么需要三级缓存?三级缓存为什么是单例工厂?
前提:
1)创建的bean可能需要进行如AOP这样的代理增强
2)正常的代理逻辑是在bean初始化阶段,通过bean后置处理器完成
- 如果bean需要进行代理(AOP),同时又存在循环引用,那么必须提前(在属性填充前)进行代理
问题是A在填充属性时,无法得知存在循环依赖的情况, 只有当B进行填充属性发现一二级缓存没有A,并且A是创建中的状态,那么B就知道我和A是存在循环依赖,此时通过A的单例工厂完成A的提前代理(AOP)的功能,至于A是否真正需要进行代理,由单例工厂自身的逻辑决定。
单例工厂的本质是什么?如何理解单例工厂?
单例工厂是封装创建逻辑或者增强逻辑的地方,并具有延时调用和条件触发的功能(回调),类似于延时锦囊牌。