首先抛出结论,Spring 目前无法解决 构造方法循环依赖,而可以解决字段的循环依赖。
例如以下两种情况:
- 字段依赖
CyclicA:
@Component
public class CyclicA {
@Autowired
private CyclicB cyclicB;
}
CyclicB
@Component
public class CyclicB {
@Autowired
private CyclicA cyclicA;
}
- 构造方法依赖
CyclicC
@Component
public class CyclicC {
private CyclicD cyclicD;
@Autowired
public CyclicC( CyclicD cyclicD) {
this.cyclicD = cyclicD;
}
}
CyclicD
public class CyclicC {
private CyclicD cyclicD;
@Autowired
public CyclicC( CyclicD cyclicD) {
this.cyclicD = cyclicD;
}
}
对于上面两个例子,1可以成功注入,而2则无法进行注入。
下面以上面两种情况来具体分析Spring 源代码。
字段注入
从前面几篇文章中,了解到,Spring
的 getBean
方法 主体流程为 实例化 Bean和 初始化 Bean 实例(包括初始化字段和 调用init方法)。
而 实例化Bean则是调用其构造方法进行调用,而初始化则是对 @Autowired
和 @Value
bean 进行赋值等操作,其原来也是对依赖进行进一步 getBean
调用。所以并不会报错。
从前面文章 分析 在getBean时候,在getSingleton时:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
- 首先会尝试从
singletonObjects
中获取 - 而后如果beanName正在创建中
isSingletonCurrentlyInCreation
,则会从earlySingletonObjects
中 获取。 - 如果
earlySingletonObjects
再一次为null,而 允许获取,则会从singletonFactories
中获取构造工厂,如果获取到了则利用其去获取构造方法。而从前面分析,singletonFactories
是的是一个lambda
表达式。
另一方面,在每一个bean实例化时候,都会进入 doCreateBean
,并且在 实例化后(populateBean前)将类信息放入缓存中:
// 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));
}
而 addSingletonFactory
则是将信息放入到 singletonFactories
、registeredSingletons
中。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
所以当A依赖B,而B又依赖A是:
- A首先完成实例化第一步,并且将自己提前曝光在
singletonFactories
和registeredSingletons
中。 - 而后,A开始执行 popularBean方法,在 对
@Autowired
字段进行解析时,会getBean
获取B,此时如果B没有创建,就会去创建 B。 - 创建B的过程中,由于B 中有对 A
@Autowired
,此时就会去getBean
获取A。 - 此时尝试
singletonObjects
没有,而尝试earlySingletonObjects
中获取没有,最后在singletonFactories
中获取到了。所以此时B拿到了A,B完成了初始化。 - 而后A从
singletonObjects
中获取到了B, 所以最终A结束了getBean获取B的操作。 - 至此,A拿到了B,B拿到了A,都完成了初始化。
构造方法注入
在getBean时候,从getBean->doGetBean->getSingleton->beforeSingletonCreation,在 beforeSingletonCreation
会有一次检查,检查 当前bean是否在创建中,如果 在创建bean时,发现自己已经在当前创建的bean池中,那么就会报错:
构造器注入和字段注入不一样,beforeSingletonCreation
执行比 实例化字段早,即在实例化之前,先检查自己是否在创建中:
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
最终singletonsCurrentlyInCreation
会同时有 CyclicC
和 CyclicD
。
非单例循环依赖
对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓 存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean 。
但是可以使用 @LookUp
在单例中使用非单例(prototype) 实例。
觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring: