在开发过程当中,遇到须要把方法调用改成异步的状况,原本觉得简单得加个@Asyn在方法上就好了,没想到项目启动的时候报了以下的错误:spring
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项目,由于原项目是有定义切面的,这里也定义一个切面:app
@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:ide
@EnableAsync @EnableAspectJAutoProxy @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
最后打好断点,开始debug。函数
从Bean建立的的起点--AbstractBeanFactory#getBean开始post
// Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName);
首先会在缓存中查找,DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference):this
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; }
这里一共有三级缓存:lua
后面两级都是为了解决循环依赖设置的,具体查找逻辑在后续其余状况下调用会说明。线程
缓存中找不到,就要建立单例:
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); ... }
建立先后分别作了这几件事:
建立单例调用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是否须要提早暴露,须要知足三个条件:一、是单例;二、支持循环依赖;三、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); ...
初始化主要分为这几步:
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。