一、什么是循环依赖
循环依赖指的是2个或2个以上的bean互相持有对方,从而形成闭环的依赖场景。例如A依赖B,B依赖C,而C又依赖A,这种情况就叫做循环依赖。
二、循环依赖的场景
1、构造器的循环依赖
2、setter循环循环依赖
三、Spring Bean的创建过程
源码见org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
Spring的单例对象的初始化分为三步
createBeanInstance
实例化:即通过构造器反射出对象实例,此时属于半成品,因为内部的属性都未初始化populateBean
填充属性:对内部依赖的属性进行填充,如果依赖另外的bean,则递归创建另外的bean并填充到自己的属性上InitializeBean
初始化:调用bean中形如initMethod
、InitializingBean
等方法
四、Spring如何解决循环依赖
1、构造器的循环依赖
根据上面bean的创建过程的分析,第一步必须反射获取bean的实例,其中反射的方式是通过构造器反射,如果构造器存在循环依赖,那么必须要查找到对应的bean才能进行实例化。例如A依赖B,B依赖C,C依赖A,A实例化需要B,则挂起A去创建B,B实例化需要C,则挂起B去创建C,C实例化需要A,而A还处于创建中,甚至还未实例化出来,因此流程死锁,无法继续执行。
2、setter循环依赖
Spring可以解决setter情况下的循环依赖
2.1、三级缓存
Spring为了解决循环依赖,创建bean的原则是不等bean创建完成就将创建bean的ObjectFactory
提早曝光到缓存中,一旦下一个bean创建时候需要时直接使用ObjectFactory
。这里涉及到三级缓存
- 一级缓存
singletonObjects
:单例对象的cache,存放可用的成品bean - 二级缓存
earlySingletonObjects
:第二级缓存,存放半成品的Bean
,半成品的Bean
是已创建对象,但是未注入属性和初始化。用以解决循环依赖 - 三级缓存
singletonFactories
:第三级缓存,存的是Bean工厂对象
,用来生成半成品的Bean
并放入到二级缓存中。用以解决循环依赖
2.2、实现细节
具体方法位于org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
//如果为空且singletonsCurrentlyInCreation存在该bean
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//锁定singletonObjects全局变量
synchronized (this.singletonObjects) {
//从earlySingletonObjects获取bean
singletonObject = this.earlySingletonObjects.get(beanName);
//如果获取不到且允许早期依赖
if (singletonObject == null && allowEarlyReference) {
//从singletonFactories获取ObjectFactory,这是因为有时候某些方法需要提前初始化的时候会
//调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果不为空
if (singletonFactory != null) {
//调用预先设定的getObject方法
singletonObject = singletonFactory.getObject();
//记录在缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//earlySingletonObjects与singletonFactories是互斥的,因此这里需要remove
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上述方法执行流程如下
- 首先从一级缓存
singletonObjects
中获取,如果获取到则直接返回, - 如果获取不到且对象也不是正在创建中,则直接返回null表示缓存中没有
- 如果对象是正在创建中,则去到二级缓存
earlySingletonObjects
中进行查找,如果获取到直接返回 - 如果获取不到且传递不允许早期依赖,直接返回null
- 如果允许早期依赖,则进一步到三级缓存
singletonFactories
中查找 - 如果未查询到,直接返回null
- 如果查询到,则调用
ObjectFactory
的getObject
方法获取对象,将对象放入二级缓存中,同时移除三级缓存。
当bean反射实例化之后,如果当前bean是单例,且允许循环依赖,且在处在创建的过程中,则会讲当前bean放到三级缓存中,目的是提前暴露,这是解决循环依赖的关键,源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
//满足以下三个条件才会调用方法addSingletonFactory
//(1)单例
//(2)允许循环依赖
//(3)bean正在创建中
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");
}
//getEarlyBeanReference目的是调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法
//也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
//例如可以实现一些AOP的逻辑,生成代理对象
//如果不是SmartInstantiationAwareBeanPostProcessor,则什么也不做
//添加bean的创建工厂到缓存中,目的是提前暴露,解决循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
addSingletonFactory
方法逻辑如下,目的是将objectFactory
放入到三级缓存singletonFactories
中,同时移除二级缓存earlySingletonObjects
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);
}
}
}
2.3、流程图解
以A依赖B,B依赖C,C依赖A为例
2.4、扩展
-
为什么需要包装一层
ObjectFactory
对象?如果创建的
Bean
有对应的代理
,那其他对象注入时,注入的应该是对应的代理对象
;但是Spring
无法提前知道这个对象是不是有循环依赖
的情况,而正常情况
下(没有循环依赖
情况),Spring
都是在创建好完成品Bean
之后才创建对应的代理
。这时候Spring
有两个选择- 不管有没有
循环依赖
,都提前
创建好代理对象
,并将代理对象
放入缓存,出现循环依赖
时,其他对象直接就可以取到代理对象并注入。 - 不提前创建好代理对象,在出现
循环依赖
被其他对象注入时,才实时生成代理对象
。这样在没有循环依赖
的情况下,Bean
就可以按着Spring设计原则
的步骤来创建
Spring选择了第二种方式,即在对象外面包一层
ObjectFactory
,提前曝光的是ObjectFactory
对象,在被注入时才在ObjectFactory.getObject
方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects
,为了防止对象在后面的初始化(init)
时重复代理
,在创建代理时,earlyProxyReferences
缓存会记录已代理的对象、 - 不管有没有
五、相关问题
1、AOP
下的循环依赖
以@Transactional为例
1.1、bean代理对象创建过程
a) bean原始对象生成
bean创建先构造器反射出一个对象(空对象,内部属性尚未组装),然后封装为一个ObjectFactory
,而这个只有当产生了循环依赖才会被调用
源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
//满足以下三个条件才会调用方法addSingletonFactory
//(1)单例
//(2)允许循环依赖
//(3)bean正在创建中
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");
}
//添加bean的创建工厂到缓存中,目的是提前暴露,解决循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
其中getEarlyBeanReference方法会返回代理对象
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
//这个方法的目的是调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法
//也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
//例如可以实现一些AOP的逻辑,生成代理对象
//如果不是SmartInstantiationAwareBeanPostProcessor,则什么也不做
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//SmartInstantiationAwareBeanPostProcessor处理
//对于注解@Transactional,存在InfrastructureAdvisorAutoProxyCreator
//它是SmartInstantiationAwareBeanPostProcessor类型的BeanPostProcessor
//因此会走getEarlyBeanReference逻辑生成代理对象
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
对于注解@Transactional,会存在InfrastructureAdvisorAutoProxyCreator
,它是SmartInstantiationAwareBeanPostProcessor
类型的BeanPostProcessor
,因此会走执行getEarlyBeanReference
逻辑生成代理对象。查看ibp.getEarlyBeanReference(exposedObject, beanName)
逻辑,源码位于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//先放入earlyProxyReferences缓存中
//earlyProxyReferences存放已生成的代理对象
this.earlyProxyReferences.put(cacheKey, bean);
//生成代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
再看wrapIfNecessary
方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//已经处理过的bean直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//如果无需增强,直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//如果是基础设施类或者配置了无需自动代理,则直接返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
//获取当前bean内需要增强的方法
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//如果存在需要增强的方法,则创建代理类
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//创建代理类
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
//如果无需代理,则设置为false,直接返回原始类型引用
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
如果bean无需代理,则直接返回原始类型,否则获取需要代理的方法生成代理对象
b) populateBean
属性填充
这里没有什么特殊地方
c) initializeBean
调用初始化方法
源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
//对特殊的bean处理:Aware、BeanClassLoaderAware、BeanFactoryAware
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//调用后置处理器的applyBeanPostProcessorsBeforeInitialization方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//激活用户自定义的init方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//调用后置处理器的applyBeanPostProcessorsAfterInitialization方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
这里主要做了以下几件事
- Aware接口处理,可以获取对应的资源,即如果bean实现了类似
BeanClassLoaderAware
、BeanFactoryAware
接口,需要组装相应的属性 - 调用
BeanPostProcessor
的postProcessBeforeInitialization
方法 - 调用用户自定义的
init
方法 - 调用
BeanPostProcessor
的postProcessAfterInitialization
方法
这里重点看下第四步,即执行BeanPostProcessor
的postProcessAfterInitialization
方法
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
//实例化后的后置处理器处理
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
前面我们说到容器中会存在InfrastructureAdvisorAutoProxyCreator
这个BeanPostProcessor
,因此我们看下它的postProcessAfterInitialization
方法逻辑,源码位于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
//根据给定的bean的class和name构建出一个key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//这里很重要
//如果之前生成的bean与当前bean不是一个或者从未生成过,则需要生成代理对象并返回
//否则直接返回原始bean
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//如果当前bean适合被代理,则进行封装
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里判断了this.earlyProxyReferences.remove(cacheKey) != bean
,当为true时,表示earlyProxyReferences
不存在该bean
或者存在与当前bean
不是一个。而我们之前由于循环依赖已经生成了代理bean,且放入到earlyProxyReferences
中 ,由于此时bean是原始类型,非代理类型,则该条件不满足,直接返回原始类型bean。
1.2、循环依赖检查
源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
//满足以下三个条件才earlySingletonExposure才会为true
//(1)单例
//(2)允许循环依赖
//(3)bean正在创建中
if (earlySingletonExposure) {
//从二级缓存中获取当前bean
//注意此时的参数allowEarlyReference是false,表示不会去三级缓存里面再去调用一次getObject了,而是在一二级获取bean
Object earlySingletonReference = getSingleton(beanName, false);
//如果获取的bean没有变化,表示没有被代理过,后面一切正常
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)
//默认是false,即不允许
//如果allowRawInjectionDespiteWrapping为false且当前bean存在被依赖的bean
//此处为二级缓存的bean与exposedObject不相同,因此需要找到当前bean所被依赖的bean,
//一旦存在则说明其他bean依赖了当前bean的二级缓存的引用,而非最终bean的引用,所以肯定存在问题
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
//获取当前bean依赖的bean集合
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 " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
该段代码是循环依赖的校验(当earlySingletonExposure
为true
才进行校验),此时bean为原始对象,exposedObject
为初始化结束后返回的对象,校验逻辑如下
-
调用
getSingleton
方法从缓存中再获取一次bean注意此时传递的参数
allowEarlyReference
是false,表示不会去三级缓存里面再去调用一次getObject
了,而是在一二级获取bean
。根据上面的分析,此时二级缓存存在,所以返回的earlySingletonReference
不为空 -
校验
exposedObject == bean
bean存放的是原始对象,而
exposedObject
根据前面的分析也是原始对象,二者相等,因此将二级缓存取出的代理代码赋值给exposedObject
,即是最终生成的对象,流程结束。
因此注入容器内的bean是代理对象,被依赖的bean注入的属性也代理对象,完美解决AOP
代理下的循环依赖。
2、@Async
下的循环依赖问题
2.1、异常原理
当使用@EnableAsync
时,会注入AsyncAnnotationBeanPostProcessor
这个后置处理器,而它只在postProcessAfterInitialization
方法中实现了代理,即在bean初始化方法执行完成后才会生成代理,因此构造函数实例化后生成的ObjectFactory
,调用它的getObject
方法返回的不是代理对象,而是原始对象,因此上面的循环依赖检查代码中,exposedObject
将是进行过代理的对象,非原始对象,exposedObject == bean
将返回false,表示bean在执行方法initializeBean时发生了变化,所以需要检查一下是否把原来的半成品给暴露出去了,一旦发现,则直接抛出异常。
2.2、与@Transactional
事务注解区别
@Transactional
使用的是自动代理创建器AbstractAutoProxyCreator
,上篇文章详细描述了,它实现了getEarlyBeanReference()
方法从而很好的对循环依赖提供了支持@Async
的代理创建使用的是AsyncAnnotationBeanPostProcessor
单独的后置处理器实现的,它只在一处postProcessAfterInitialization()
实现了对代理对象的创建,因此若出现它被循环依赖
了,就会抛出异常。
2.3、如何解决
根据上面的分析,解决方案如下四种(以【A是被@Async
修饰的类,B依赖A,A又依赖B】为例)
-
将
allowRawInjectionDespiteWrapping
设置为true(不推荐)@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true); } }
allowRawInjectionDespiteWrapping
表示是否允许包装代理类的原始注入,设置为true表示允许,上面的检查依赖就不会执行,也就不会抛出异常,但是这样会导致B将依赖的是A的原始对象,而不是代理类型,从而导致B没法使用A异步方法的异步特性,因此非常不推荐。 -
使用
@Lazy
修饰(推荐)在B类上的A属性上加上@Lazy,表示延迟加载,这样容器启动不会保持,B依赖的也是A的代理类,A在容器内也是个代理类。但是有一点需要注意,B内持有的A和容器中的A不是同一个代理类。该方式建议。
-
@ComponentScan(lazyInit = true)
解决原理同上,但是作用范围太广,容易误伤。
-
不要让
@Async
的Bean参与循环依赖实际业务开发很难避免,该方案需要具体情况具体对待。
参考资料
https://blog.csdn.net/f641385712/article/details/92801300
https://blog.csdn.net/f641385712/article/details/92797058