目录
- ㈠ 先看一看循环依赖的几种形态
- ㈡ Spring对循环依赖的支持
- ⑴ 三级缓存
- ⑵ 创建Bean的简单流程
- ㈢ @DependsOn 注解
- ㈣ 循环依赖分析
- ⑴ 单例Bean全构造函数注入
- ⑵ 单例Bean构造函数+setter方法注入
- ⑶ 单例setter方法/字段注入
- ⑷ 存在代理的单例setter方法注入
- ⑸ 多例Bean之间的循环依赖
- ⑹ JavaConfig不能配置循环依赖
- ㈤ 总结
声明:文章内容仅代表个人对源码的理解和观点,可能存在理解错误的地方,欢迎留言探讨、指正。不喜勿喷,谢谢。
个人用的是 v5.2.4.RELEASE 版本的源码,因为几年前阅读时已经写了一些注释,所以没有拉取最新的源码,与最新的源码会存在一定的差异。在本文中的Bean,如果没有特殊说明,都是指非懒加载的单例Bean。
个人习惯了将原型模式的Bean叫做多例Bean,大家在看文章的时候,将就将就。
循环依赖就是一个或多个Bean之间相互存在直接或间接的依赖关系,这种依赖关系形成了一个环形结构。
㈠ 先看一看循环依赖的几种形态
单个Bean自身循环依赖
两个Bean之间的循环依赖。
多个Bean之间的循环依赖,下图是3个Bean之间的循环依赖,有时候可能是四个、五个甚至更多个Bean。这种形态如果不熟悉代码,是比较难发现的。
㈡ Spring对循环依赖的支持
Spring默认支持单例Bean的循环依赖,如果将BeanFactory
的allowCircularReferences
属性设置为false
,则可以禁止循环依赖。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
private boolean allowCircularReferences = true;
public void setAllowCircularReferences(boolean allowCircularReferences) {
this.allowCircularReferences = allowCircularReferences;
}
}
⑴ 三级缓存
Spring通过三级缓存
的方式实现循环依赖,先看一下Spring的三级缓存,从缓存的名字就可以看出,只有单例Bean才会存入缓存中。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/**
* Cache of singleton objects: bean name to bean instance.
* 单例对象的缓存:bean名称 -> bean实例 的映射关系。
* 一级缓存
* 保存的是实例化、属性填充、初始化、AOP代理都已完成的单例Bean。
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* Cache of singleton factories: bean name to ObjectFactory.
* 单例对象工厂的缓存:bean名称 -> 对象工厂 的映射关系。
* 三级缓存
* 存的是对象工厂(ObjectFactory),对象工厂可以返回原生Bean,也可以返回AOP代理对象,一般称为提前暴露的Bean引用。
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* Cache of early singleton objects: bean name to bean instance.
* 早期单例对象的缓存:bean名称 -> bean实例 的映射关系。
* 二级缓存
* 保存的是已实例化 或 已是AOP代理对象,但未完成属性填充、初始化的单例Bean,这种对象一般称为早期对象。
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}
单例Bean实例化完成之后,属性填充之前,将Bean包装成一个ObjectFactory
对象存入三级缓存,用于提前暴露Bean的引用。
// DefaultSingletonBeanRegistry
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) { // 一级缓存中没有Bean才能放入三级缓存
this.singletonFactories.put(beanName, singletonFactory); // 将Bean工厂添加到三级缓存
this.earlySingletonObjects.remove(beanName); // 删除二级缓存中的对象
this.registeredSingletons.add(beanName); // 添加到已注册列表
}
}
}
单例Bean创建完成之后,会将Bean存到一级缓存,并删除二级、三级缓存中的值。
// DefaultSingletonBeanRegistry
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject); // 加入一级缓存
this.singletonFactories.remove(beanName); // 删除三级缓存
this.earlySingletonObjects.remove(beanName); // 删除二级缓存
this.registeredSingletons.add(beanName); // 添加到已注册列表
}
}
如果正在创建的Bean依赖另外一个Bean时,会通过BeanFactory.getBean()
方法从IoC容器中获取Bean,此时会先从缓存中获取,缓存中没有才会执行Bean的创建流程。
// DefaultSingletonBeanRegistry 从缓存中获取单例Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 先从一级缓存中找
Object singletonObject = this.singletonObjects.get(beanName);
// 一级缓存没有,判断beanName对应的Bean是不是当前正在创建的Bean,如果是,则先从二级和三级缓存中查找
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
/*
* 从二级缓存中找,二级缓存中保存了所有提前曝光的单例
* 何为提前曝光的对象(早期对象):通过ObjectFactory创建出来的,还没给bean的属性进行赋值和初始化的对象就是早期对象
*/
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存也没有,并且允许早期依赖,则从三级缓存中找
if (singletonObject == null && allowEarlyReference) {
/*
* 如果允许早期依赖,从三级缓存中找到Bean的ObjectFactory对象
* 在getBean的过程中,当bean实例化完成之后,会将早期对象包裹成一个ObjectFactory缓存到三级缓存中
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
// 存在于三级缓存,则取出提前暴露的对象放入二级缓存
if (singletonFactory != null) {
/*
* 包装ObjectFactory存入三级缓存的源码在 AbstractAutowireCapableBeanFactory 类中,源码如下:
* addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
* 所以,ObjectFactory.getObject()会调用 AbstractAutowireCapableBeanFactory.getEarlyBeanReference()
* 若启用了AOP功能,会执行 AbstractAutoProxyCreator.getEarlyBeanReference() 方法,如果Bean存在匹配的切点,最终返回一个AOP代理对象
* 注意:能够生成AOP代理对象的处理类有多个,此处只能返回通过 AbstractAutoProxyCreator 生成的AOP对象,若是需要使用其它类生成AOP代理对象,这里是无法处理的,返回的是原对象
*/
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject); // 早期对象存入二级缓存中
this.singletonFactories.remove(beanName); // 删除三级缓存中的FactoryBean
}
}
}
}
return singletonObject;
}
⑵ 创建Bean的简单流程
在介绍循环依赖之前,先看一看创建Bean的简单流程,循环依赖中会反复的用到下图中的一些步骤,在后续的流程图中,可能会省略掉其中的一些步骤,以便看起来比较简洁。
绿色与缓存操作相关。
橙色与多例的循环依赖检查有关。
红色与代理对象的循环依赖检查有关。
黄色与构造函数循环依赖检查有关。
㈢ @DependsOn 注解
在验证循环依赖注入之前,先了解一下 @DependsOn
注解,因为在后面的验证过程中会用到。
使用方法如下代码所示,在创建Bean serviceA 之前,会先创建 @DependsOn
注解中指定的 Bean(serviceB, serviceC)。
@Component
@DependsOn(value = {"serviceB, serviceC"})
public class ServiceA {
public ServiceA() {
}
}
从注解的名字来看,是用于配置依赖关系的,从代码执行结果来看,此注解还可以人为配置Bean的创建顺序。@DependsOn
注解不支持循环依赖,否则创建Bean时会抛出异常(注意:是创建Bean时才会异常,不能说是启动时异常,例如将Bean配置成懒加载,启动并不会出现异常),如果配置的Bean不存在,也会抛出异常。如下代码所示:
@Component
@DependsOn(value = {"serviceB"})
public class ServiceA {
public ServiceA() {
}
}
@Component
@DependsOn(value = {"serviceA"})
public class ServiceB {
public ServiceB() {
}
}
@DependsOn
循环依赖抛出异常的源码:
// AbstractBeanFactory
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 。。。。。。 省略大量代码 。。。。。。
try {
// 获取要实例化的Bean的BeanDefinition对象
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 初始化 depends-on 指定依赖的 Bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 判断是否存在循环依赖,depends-on 不能有循环依赖,否则抛出异常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName); // 注册Bean的依赖关系
try {
getBean(dep); // 初始化 depends-on 依赖的 bean
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 创建Bean 。。。。。。 省略大量代码 。。。。。。
}
㈣ 循环依赖分析
目前常用的注入方式有构造函数注入和setter方法注入,先验证一下Spring对这两种注入方式的支持。
⑴ 单例Bean全构造函数注入
测试代码如下,当创建Bean时,会抛出异常:
@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
异常信息如下,可以看出原始异常是 BeanCurrentlyInCreationException
:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceA' defined in file [XXX\ServiceA.class]:
Unsatisfied dependency expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceB' defined in file [XXX\ServiceB.class]:
Unsatisfied dependency expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?
抛出异常的源码:
// DefaultSingletonBeanRegistry
protected void beforeSingletonCreation(String beanName) {
/*
* inCreationCheckExclusions 从 singletonsCurrentlyInCreation 中排除的 beanName,在AOP的处理中可能会有值,一般情况下是空的
* singletonsCurrentlyInCreation 是一个Set,用于保存当前正常创建的Bean的名称,当已存在相同的值时,Set.add()返回false
*/
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
全构造函数循环依赖,抛出异常的流程:
从上图中可以看出,构造函数循环依赖时,抛出异常的关键是 DefaultSingletonBeanRegistry
类中的 singletonsCurrentlyInCreation
属性:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// 当前正在创建中的单例 bean 的 beanName 集合
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}
从上面的分析可以看出,Spring不支持全构造函数的循环依赖。实例化serviceA时,要先实例化serviceB,实例化serviceB时,又发现要先实例化serviceA,这样就造成了死循环。
⑵ 单例Bean构造函数+setter方法注入
此模式也可以叫做部分构造函数注入。
如果将上面的注入方式修改成如下形式:一个是构造函数注入,一个是setter方法(或者字段)注入,使用 @DependsOn
或者 @Lazy
保证serviceB先被实例化,则可以循环依赖成功,因为实例化serviceA时,serviceB已经实例化了,并且已经保存在三级缓存中,直接取值即可。
如果是先实例化serviceA,仍然会抛出异常。
@DependsOn("serviceB")
// @Lazy
@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上面代码的执行流程如下图所示,有几个点需要说明:
- 除黄色步骤以外,其它步骤都是由检查 serviceA 的
@DependsOn
触发的。 - 在检查
@DependsOn
的前后都会检查缓存中是否已有Bean实例,检查@DependsOn
之前从三个缓存中查找Bean,检查@DependsOn
之后只从一级缓存中查找Bean。 - 一个Bean的
@DependsOn
可能会被检查多次。
在面试的时候,经常有人回答说Spring不支持构造函数循环依赖。其实这是不完全正确的,Spring只是不支持全构造函数的循环依赖,如果是部分构造函数的循环依赖,还是支持的,只要保证使用构造函数循环依赖的Bean后实例化。
⑶ 单例setter方法/字段注入
测试代码如下:
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上面代码循环依赖的流程如下图所示:
在面试时,问到Spring的循环依赖,大部分人都会回答Spring支持单例Bean的循环依赖。然后我会接着问:是单例就肯定能成功吗?得到的答案基本都是肯定的。其实这不完全正确,我们看一看下面的场景。
⑷ 存在代理的单例setter方法注入
测试代码如下:
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Async
public void asyncExecute () {
}
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上面的代码在创建Bean时,会抛出 BeanCurrentlyInCreationException
异常:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'serviceA':
Bean with name 'serviceA' has been injected into other beans [serviceB] 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
抛出异常的源码分析:
// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
/*
* 第一步:实例化Bean
*/
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 实例化后的Bean对象,这里是早期对象,还没有进行属性注入
final Object bean = instanceWrapper.getWrappedInstance();
// 。。。。。。省略部分代码。。。。。。
// 判断该对象是否允许暴露早期对象,解决循环依赖的关键步骤
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName)); // 如果当前是单例,且允许循环依赖而且当前bean处于创建状态,那么允许暴露早期对象
// 如果需要提前暴露单例Bean的早期对象,则将该Bean放入singletonFactory 单例工厂(三级缓存)中,解决循环引用的问题
if (earlySingletonExposure) {
// 。。。。。。日志打印,省略。。。。。。
/*
* 解决循环引用的问题,将早期对象包装成一个ObjectFactory对象,并缓存到三级缓存中(key是beanName,value是一个ObjectFactory),提前暴露对象的引用
* 当从三级缓存中获取早期对象,调用ObjectFactory.getObject()方法时,会执行 getEarlyBeanReference 方法,
* 若启用了AOP功能,会执行 AbstractAutoProxyCreator.getEarlyBeanReference() 方法,如果Bean存在匹配的切点,最终返回一个AOP代理对象
* 注意:能够生成AOP代理对象的处理类有多个,此处只能返回通过 AbstractAutoProxyCreator 生成的AOP对象,若是需要使用其它类生成AOP代理对象,这里是无法处理的,返回的是原对象
* 例如,AsyncAnnotationBeanPostProcessor 为使用了 @Async 注解的Bean生成代理对象,此处是无法处理的,在Bean初始化之后通过 BeanPostProcessor 的后置处理方法生成代理
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
/*
* 第二步:填充属性
*/
populateBean(beanName, mbd, instanceWrapper);
// 第三步:初始化Bean。调用初始化方法,完成bean的初始化操作(AOP发生在这个步骤)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
// 。。。。。。异常处理,省略 。。。。。。
}
// 是否允许提前暴露单例Bean,如果允许,则需要做一次循环依赖之间的校验
if (earlySingletonExposure) {
/*
* 从缓存中获取Bean,由于传递的 allowEarlyReference = false,所以只会在一级和二级缓存查找,不会从三级缓存中查找提前暴露的引用。
* 如果不存在循环依赖,此处获取的值为null,,反过来说,如果此处获取到了值,就存在循环依赖。
*
* 假设ClassA依赖ClassB,ClassB又依赖ClassA,而且先创建ClassA。
* ClassA实例化,加入三级缓存,在ClassA属性填充的时候,需要注入ClassB,于是去创建ClassB,
* ClassB实例化,加入三级缓存,在ClassB属性填充的时候,需要注入ClassA,于是从三级缓存中取出ClassA,这里取出的可能是ClassA的代理对象,将ClassA放入二级缓存,并返回ClassA,
* ClassB属性填充完成,初始化完成,然后ClassB走到了这里,此时 exposedObject 是ClassB初始化之后的对象,它可能是一个AOP代理对象,
* ClassB此时只是存在于三级缓存中,由于 allowEarlyReference = false,禁止查找三级缓存中的早期引用,
* 所以,从缓存中是获取不到ClassB的实例,跳过这一步的校验,在外层ClassB会被放入一级缓存,然后删除二级、三级缓存中的ClassB实例。
* 此时回到了ClassA的属性填充流程,从一级缓存拿到ClassB,ClassA属性填充完成,初始化完成,然后走到这里,
* 此时,ClassA已经存在二级缓存,所以这里能够获取到ClassA提前暴露的实例,可能是ClassA的AOP代理对象。
*/
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) { // 上面的例子,ClassA会进入这里
/*
* 进入这里,就说明存在循环依赖
* exposedObject 是初始化之后的对象,它可能是一个 AbstractAutoProxyCreator 或者其它处理器(AsyncAnnotationBeanPostProcessor)生成的AOP代理对象。
* 而 bean 指向的是刚实例化的对象,如果两者相等,说明bean在初始化的过程中没有产生代理对象,
* 于是将 exposedObject 指向二级缓存中的对象,此对象可能是原生的bean,也可能是通过 AbstractAutoProxyCreator 生成的代理对象,
* 所以这一步走完之后, exposedObject 是指向终的Bean实例了,如果需要代理则已经是代理对象,如果无需代理则是原生Bean。
* 如果此处不相等,则说明在初始化的过程中产生了代理对象,并且不是 AbstractAutoProxyCreator 生成的,可能是 AsyncAnnotationBeanPostProcessor。
*/
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
/*
* allowRawInjectionDespiteWrapping 是否在循环依赖的情况下注入原始 bean 实例,默认为 false
* hasDependentBean() 方法用于判断是否有Bean依赖了当前Bean,例如这里判断是否有Bean依赖ClassA
* 进入这里,说明循环依赖中的Bean在初始化的时候,被其他BeanPostProcessor处理器生成了代理对象,
* 所以这一步校验的是 依赖当前bean 的 其它bean是否已经创建完成,如果创建完成,那么它所依赖的Bean与实际的Bean不相同,需要抛出异常。
* 对上面的例子做个扩展进行说明,ClassA中使用了@Async注解,所以在初始化的时候被 AsyncAnnotationBeanPostProcessor 生成了代理对象,
* 而ClassB中注入的是ClassA的原生对象,两个对象不相同,所以抛出异常。
*/
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 依赖当前 Bean (ClassA) 的 其它beanName(ClassB)
String[] dependentBeans = getDependentBeans(beanName);
// 用于保存已经创建完成的beanName,上面的例子中 ClassB 就会被保存到这里
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
/*
* 判断ClassB是否已经创建完成
* ClassB 已经创建完成,removeSingletonIfCreatedForTypeCheckOnly方法返回false,ClassB加入集合;
* ClassB 未创建完成,尝试清除ClassB在三个缓存中的值,所以ClassB在后续会重新走一遍创建流程,removeSingletonIfCreatedForTypeCheckOnly返回true,ClassB不加入集合
*/
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
/*
* 以上面的例子说明这个异常:
* 名称为“ClassA”的 Bean 已作为循环依赖的一部分注入到其他 bean [ClassB] 中,但在注入之后,ClassA被生成了AOP代理对象。
* 这意味着 ClassB 依赖的不是 ClassA 的最终版本。
*/
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// 。。。。。。Register bean as disposable. 。。。。。。
return exposedObject;
}
抛出异常的流程图:
从上面的分析可以看出,存在代理对象的循环依赖抛出异常,主要关系到两个属性:
一个是 AbstractBeanFactory
类中的 alreadyCreated
,用于先行标记 Bean 已经创建完成。
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// 已至少创建一次的 bean 的名称。
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}
一个是 DefaultSingletonBeanRegistry
类中的 dependentBeanMap
,用于记录Bean之间的依赖关系
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
// Bean的依赖关系。key:被依赖的bean的beanName(keyBean),value:需要依赖keyBean的 bean的beanName(valueBean)。valueBean 依赖 keyBean
// 例如,beanA 依赖 beanX,beanB 依赖 beanX,那么 key 是 beanX,value 是存放 beanA 和 beanB 的集合。
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
}
和部分构造函数一样,如果我们使用 @DependsOn
或者 @Lazy
注解,让serviceB先实例化,在serviceB进行属性填充时,serviceA会进行实例化,并先进行初始化,此时就会产生代理对象,那么注入到serviceB中的就是最终的serviceA的代理对象,此时就不会出现异常。修改后的代码如下:
@DependsOn("serviceB")
// @Lazy
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Async
public void asyncExecute () {
}
}
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
需要特别说明的点:
如果代理对象是通过AbstractAutoProxyCreator
的子类生成的,例如我们自定义的AOP,不管哪个Bean先实例化、属性填充、初始化,循环依赖都没有问题的。
原因就在于提前暴露的ObjectFactory
,在执行getObject()方法时,会执行到AbstractAutoProxyCreator.getEarlyBeanReference()
方法,先将原生的Bean缓存起来(earlyProxyReferences
),然后生成一个代理对象返回,存入二级缓存中即是AOP代理对象了。在Bean初始化阶段,执行AbstractAutoProxyCreator
子类的后置处理器时,删除缓存(earlyProxyReferences
),并判断原生Bean是否存在缓存中,若存在,则直接返回,不再创建代理对象。@Async
注解是通过AsyncAnnotationBeanPostProcessor
处理器生成代理对象,此处理器不是AbstractAutoProxyCreator
的子类。
⑸ 多例Bean之间的循环依赖
前面分析的都是单例Bean的循环依赖,现在分析一下多例的循环依赖。测试代码如下:
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ServiceB {
@Autowired
private ServiceA serviceA;
}
上面的代码在获取Bean时,会抛出如下异常,可以看出,原始异常仍然是 BeanCurrentlyInCreationException
:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceA': Unsatisfied dependency expressed through field 'serviceB'; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceB': Unsatisfied dependency expressed through field 'serviceA'; nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?
异常是在 AbstractBeanFactory.doGetBean()
方法中抛出的:
// AbstractBeanFactory
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 转换beanName
final String beanName = transformedBeanName(name);
// 省略大量代码
if (sharedInstance != null && args == null) {
// 缓存中已有Bean,走这个分支,省略 。。。。。。
}
else { // 没有缓存中没有Bean,会进入这个分支
// 判断beanName是否是一个已经正在创建的多例Bean,成立返回true,抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 省略好多好多代码 。。。。。。
}
// 省略好多代码。。。。。。
return (T) bean;
}
多例循环依赖抛出异常的流程图如下,由于多例不会添加到三个缓存中,所以关于缓存的部分就不画了:
从上面的图中可以看出 AbstractBeanFactory
类中的 prototypesCurrentlyInCreation
属性发挥着重要的作用,此属性的源码如下:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
// 当前线程"正在创建中的多例Bean的缓存"
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
}
多例Bean之间的循环依赖,如果不抛出异常,就有点子子孙孙无穷尽也的味道了。
如果是一个多例Bean和一个单例Bean之间的循环依赖,Spring是支持的,因为单例Bean在IoC容器启动的时候进行创建,实例化之后,存入三级缓存中,属性填充时发现依赖多例,于是创建一个多例Bean,多例Bean属性填充时从三级缓存中得到单例Bean,完成属性填充和初始化,接着单例Bean也能完成了属性填充和初始化,并存入一级缓存。
⑹ JavaConfig不能配置循环依赖
下面的这个示例代码,会出现 java.lang.StackOverflowError
异常:
@Configuration
public class CircularDependencyConfiguration {
@Bean
public ServiceA serviceA() {
ServiceA serviceA = new ServiceA();
serviceA.setServiceB(serviceB());
return serviceA;
}
@Bean
public ServiceB serviceB() {
ServiceB serviceB = new ServiceB();
serviceB.setServiceA(serviceA());
return serviceB;
}
}
如果改成下面这种形式,会抛出 BeanCurrentlyInCreationException
异常,这个好理解,因为这种方式配置的Bean,在创建过程中被识别为构造函数注入,代码执行流程也与构造函数注入相似。
@Configuration
public class CircularDependencyConfiguration {
@Bean
public ServiceA serviceA(ServiceB serviceB) {
ServiceA serviceA = new ServiceA();
serviceA.setServiceB(serviceB);
return serviceA;
}
@Bean
public ServiceB serviceB(ServiceA serviceA) {
ServiceB serviceB = new ServiceB();
serviceB.setServiceA(serviceA);
return serviceB;
}
}
这种情况的流程图就不画了,画得手疼。
㈤ 总结
对上面分析的场景做一个简单的总结:
- 全构造函数注入的循环依赖,Spring不支持。
- 一个使用构造函数注入,一个使用setter方法注入的循环依赖,借助
@DependsOn
或@Lazy
注解,让使用setter方法注入的Bean先实例化,则可以完成循环依赖。 - 全setter方法注入,在不会生成AOP代理对象,或者只会通过
AbstractAutoProxyCreator
的子类生成代理对象的场景下,Spring是支持的。 - 全setter方法注入,存在非
AbstractAutoProxyCreator
子类生成代理对象的场景,借助@DependsOn
或@Lazy
注解,让代理对象先被创建出来,则可以完成循环依赖。 - 全多例Bean之间的循环依赖,Spring不支持。
- 一个多例Bean,一个单例Bean之间的循环依赖,Spring支持。
- JavaConfig中配置的循环依赖,Spring不支持。