共计 10744 个字符,预计需要花费 27 分钟才能阅读完成。
源起
在开发过程中,遇到需要把方法调用改为异步的情况,本来以为简单得加个 @Asyn 在方法上就行了,没想到项目启动的时候报了如下的错误:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name ‘customerServiceImpl’:
Bean with name ‘customerServiceImpl’ has been injected into other beans [customerServiceImpl,followServiceImpl,cupidService] 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.
看了下好像报的是循环依赖的错误,但是 Spring 单例是支持循环依赖的,当时一脸懵逼。拿着报错去百度了下,说是多个动态代理导致的循环依赖报错,也找到了报错的地点,但是还是不明白为什么会这样,所以打算深入源码探个究竟,顺便回顾下 Bean 的获取流程和循环依赖的内容。
模拟场景
用 SpringBoot 新建一个 demo 项目,因为原项目是有定义切面的,这里也定义一个切面:
@Aspect
@Component
public class TestAspect {
@Pointcut(“execution(public * com.example.demo.service.CyclicDependencyService.sameClassMethod(..))”)
private void testPointcut() {}
@AfterReturning(“testPointcut()”)
public void after(JoinPoint point) {
System.out.println(“ 在 ” + point.getSignature() + “ 之后干点事情 ”);
}
}
然后新建一个注入自己的 Service 构成循环依赖,然后提供一个方法满足切点要求,并且加上 @Async 注解:
@Service
public class CyclicDependencyService {
@Autowired
private CyclicDependencyService cyclicDependencyService;
public void test() {
System.out.println(“ 调用同类方法 ”);
cyclicDependencyService.sameClassMethod();
}
@Async
public void sameClassMethod() {
System.out.println(“ 循环依赖中的异步方法 ”);
System.out.println(“ 方法线程:” + Thread.currentThread().getName());
}
}
还有别忘了给 Application 启动类加上 @EnableAsync 和 @EnableAspectJAutoProxy:
@EnableAsync
@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
最后打好断点,开始 debug。
debug
从 Bean 创建的的起点 –AbstractBeanFactory#getBean 开始
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
首先会在缓存中查找,DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference):
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,保存初始化完成的单例 bean 实例;
earlySingletonObjects,保存提前曝光的单例 bean 实例;
singletonFactories,保存单例 bean 的工厂函数对象;
后面两级都是为了解决循环依赖设置的,具体查找逻辑在后续其他情况下调用会说明。
缓存中找不到,就要创建单例:
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
调用 DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory):
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
…
beforeSingletonCreation(beanName);
…
singletonObject = singletonFactory.getObject();
…
afterSingletonCreation(beanName);
…
addSingleton(beanName, singletonObject);
…
}
创建前后分别做了这几件事:
前,beanName 放入 singletonsCurrentlyInCreation,表示单例正在创建中
后,从 singletonsCurrentlyInCreation 中移除 beanName
后,将创建好的 bean 放入 singletonObjects,移除在 singletonFactories 和 earlySingletonObjects 的对象
创建单例调用 getSingleton 时传入的工厂函数对象的 getObject 方法,实际上就是 createBean 方法,主要逻辑在 AbstractAutowireCapableBeanFactory#doCreateBean 中:
…
instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = instanceWrapper.getWrappedInstance();
…
// 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);
}
…
if (earlySingletonExposure) {
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 ” +
“‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.”);
}
}
}
}
可以看到报错就是在这个方法里抛出的,那么这个方法就是重点中的重点。
首先实例化单例,instantiate,只是实例化获取对象引用,还没有注入依赖。我 debug 时记录的 bean 对象是 CyclicDependencyService@4509;
然后判断 bean 是否需要提前暴露,需要满足三个条件:1、是单例;2、支持循环依赖;3、bean 正在创建中,也就是到前面提到的 singletonsCurrentlyInCreation 中能查找到,全满足的话就会调用 DefaultSingletonBeanRegistry#addSingletonFactory 把 beanName 和单例工厂函数对象(匿名实现调用 AbstractAutowireCapableBeanFactory#getEarlyBeanReference 方法)放入 singletonFactories;
接着就是注入依赖,填充属性,具体怎么注入这里就不展开了,最后会为属性 cyclicDependencyService 调用 DefaultSingletonBeanRegistry.getSingleton(beanName, true),注意这里和最开始的那次调用不一样,isSingletonCurrentlyInCreation 为 true,就会在 singletonFactories 中找到 bean 的单例工厂函数对象,也就是在上一步提前暴露时放入的,然后调用它的匿名实现 AbstractAutowireCapableBeanFactory#getEarlyBeanReference:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
方法逻辑就是挨个调用实现了 SmartInstantiationAwareBeanPostProcessor 接口的后置处理器(以下简称 BBP)的 getEarlyBeanReference 方法。一个一个 debug 下来,其他都是原样返回 bean,只有 AnnotationAwareAspectJAutoProxyCreator 会把原 bean(CyclicDependencyService@4509)存在 earlyProxyReferences,然后将 bean 的代理返回(debug 时记录的返回对象是 CyclicDependencyService$$EnhancerBySpringCGLIB$$6ed9e2db@4740)并放入 earlySingletonObjects,再赋给属性 cyclicDependencyService。
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
属性填充完成后就是调用初始化方法 AbstractAutowireCapableBeanFactory#initializeBean:
…
invokeAwareMethods(beanName, bean);
…
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
…
invokeInitMethods(beanName, wrappedBean, mbd);
…
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
…
初始化主要分为这几步:
如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware,把相应的资源放入 bean;
顺序执行 BBP 的 postProcessBeforeInitialization 方法;
如果实现了 InitializingBean 就执行 afterPropertiesSet 方法,然后执行自己的 init-method;
顺序执行 BBP 的 postProcessAfterInitialization。
debug 的时候发现是第 4 步改变了 bean,先执行 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization:
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里会获取并移除之前存在 earlyProxyReferences 的 bean(CyclicDependencyService@4509),因为和当前 bean 是同一个对象,所以什么都没做直接返回。随后会执行 AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization:
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
}
先判断 bean 是否有需要代理,因为 CyclicDependencyService 有方法带有 @Async 注解就需要代理,返回代理对象是 CyclicDependencyService$$EnhancerBySpringCGLIB$$e66d8f6e@5273。
返回的代理对象赋值给 AbstractAutowireCapableBeanFactory#doCreateBean 方法内的 exposedObject,接下来就到了检查循环依赖的地方了:
if (earlySingletonExposure) {
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 ” +
“‘getBeanNamesOfType’ with the ‘allowEagerInit’ flag turned off, for example.”);
}
}
}
}
首先从 earlySingletonObjects 里拿到前面属性填充时放入的 bean 代理(CyclicDependencyService$$EnhancerBySpringCGLIB$$6ed9e2db@4740),不为空的话就比较 bean 和 exposedObject,分别是 CyclicDependencyService@4509 和 CyclicDependencyService$$EnhancerBySpringCGLIB$$e66d8f6e@5273,很明显不是同一个对象,然后会判断 allowRawInjectionDespiteWrapping 属性和是否有依赖的 bean,然后判断这些 bean 是否是真实依赖的,一旦存在真实依赖的 bean,就会抛出 BeanCurrentlyInCreationException。
总结
总结下 Spring 解决循环依赖的思路:在创建 bean 时,对于满足提前曝光条件的单例,会把该单例的工厂函数对象放入三级缓存中的 singletonFactories 中;然后在填充属性时,如果存在循环依赖,必然会尝试获取该单例,也就是执行之前放入的工厂函数的匿名实现,这时候拿到的有可能是原 bean 对象,也有可能是被某些 BBP 处理过返回的代理对象,会放入三级缓存中的 earlySingletonObjects 中;接着 bean 开始初始化,结果返回的有可能是原 bean 对象,也有可能是代理对象;最后对于满足提前曝光的单例,如果真的有提前曝光的动作,就会去检查初始化后的 bean 对象是不是原 bean 对象是同一个对象,只有不是的情况下才可能抛出异常。重点就在于存在循环依赖的情况下,初始化过的 bean 对象是不是跟原 bean 是同一个对象。
从以上的 debug 过程可以看出,是 AsyncAnnotationBeanPostProcessor 这个 BBP 在初始化过程中改变了 bean,使得结果 bean 和原 bean 不是一个对象,而 AnnotationAwareAspectJAutoProxyCreator 则是在填充属性获取提前曝光的对象时把原始 bean 缓存起来,返回代理的 bean。然后在初始化时执行它的 postProcessAfterInitialization 方法时如果传入的 bean 是之前缓存的原始 bean,就直接返回,不进行代理。如果其他 BBP 也都没有改变 bean 的话,初始化过后的 bean 就是跟原始 bean 是同一个对象,这时就会把提前曝光的对象(代理过的)作为最终生成的 bean。