「Spring源码」循环依赖解析&遇到的bug分析

  • 本篇分析内容
    • Spring循环依赖源码解析
    • @Async引发的bug分析
闲聊 本来打算开开信心摸鱼看源码了,突然被问了一个bug,这好奇心就忍不住了,循环依赖照理来说Spring应该处理好了,怎么还会有问题呢?正好之前鸽了好久没写了,就借此机会分析下,先贴报错:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'XXXXXA': Bean with name 'XXXXXA' has been injected into other beans [XXXXXB] 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循环依赖源码解析
@Component public class A { @Autowired private B b; public void sayHello(){ System.out.println("hello"); } }@Service public class B { @Autowired private C c; @Async public void sayHello(){ System.out.println("hello"); } }@Service public class C { @Autowired private D d; public void sayHello(){ System.out.println("hello"); } }@Component public class D { @Autowired private A a; public void sayHello(){ System.out.println("hello"); } }@ComponentScan("org.springframework.test.self.annotation") public class Demo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(A.class); applicationContext.register(B.class); applicationContext.register(C.class); applicationContext.register(D.class); applicationContext.refresh(); A bean = (A) applicationContext.getBean("a"); bean.sayHello(); } }

大致流程
「Spring源码」循环依赖解析&遇到的bug分析
文章图片

前置说明
首先我先申明下,真的不知道谁取得一/二/三级缓存这几个名字的,但是很恐怖的事大家都在用,就是几个HashMap整这么花里胡哨的,Spring里面缓存有很多,叫四级,五级,六级。。。。。。n+级?
为了方便大家理解还是沿用了这几个名字了。我看了下基本就是这个方法里面用到的这三个缓存org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean),英文原版注释也放出来,各位怎么方便记怎么来
//一级缓存(?)存放创建完成,初始完成的bean /** Cache of singleton objects: bean name to bean instance. */ private final Map singletonObjects = new ConcurrentHashMap<>(256); //三级缓存(?)实例创建完,通过lamdba表达式,封装成ObjectFactory /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map> singletonFactories = new HashMap<>(16); //二级缓存(?),循环依赖中用到,三级缓存中取出刚实例化的bean /** Cache of early singleton objects: bean name to bean instance. */ private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);

流程分析
总体流程 截图工具有限,无法截长图,答题流程就是如下,创建A->发现需要B->创建B...上面已经给了大概的流程图了,感觉已经很详细了,这里就贴一下程序调用栈

核心程序为org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
前面Spring源码也是详细分析了,感兴趣的可以自行阅读,这里呢讲一个大概流程。
创建(实例化)Bean->加入三级缓存->依赖注入->初始化->循环依赖检查->返回
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {// Instantiate the bean.持有创建出来的Bean对象 BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) {//单例,先把缓存中的同名Bean清除 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } //TODO 实际创建bean if (instanceWrapper == null) { //可以理解成调用构造函数创建实例,封装进了BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; }// Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } }// Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // 缓存单例,以便即使在由 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); //bean初始化,调用调用各种前置处理,后置处理等等 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } }//如果允许解决循环依赖的标识 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { //依赖这个bean的所有bean String[] dependentBeans = getDependentBeans(beanName); Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { //如果依赖这个bean的bean创建完成,而初始化完成的bean和注入给其他bean不一样,spring就认为是异常了 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 " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } }// Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); }return exposedObject; }

查询缓存 对于循环依赖A->B->C->D->A到最后一步创建A的时候,这时候肯定不能继续下去,因为再下去就死循环了
这个时候缓存的情况:
singletonObjects(一级缓存中)没有A,
earlySingletonObjects(二级缓存中)没有A,
singletonFactories(三级缓存中)有A
此时singletonFactories(三级缓存中)有但是还没有初始化,这个时候,就把还没有初始化的bean返回。让其他几个完成创建挨个出栈。三级缓存也相应的删除,加入到二级缓存中,到时候普通缓存查询也能够返回该实例。
「Spring源码」循环依赖解析&遇到的bug分析
文章图片

protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock|快速检没有锁的现有实例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock|在完整的单例锁中一致地创建早期引用 singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }

至此,循环依赖总体流程分析结束。下面我们那一个同事询问我的bug分析分析
@Async引发的bug分析 报错代码贴了,就是上面org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean中下面抛出异常的那一段
//如果允许解决循环依赖的标识 if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { //依赖这个bean的所有bean String[] dependentBeans = getDependentBeans(beanName); Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { //如果依赖这个bean的bean创建完成,而初始化完成的bean和注入给其他bean不一样,spring就认为是异常了 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 " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } }

那肯定exposedObject == bean不相等了。exposedObject = initializeBean(beanName, exposedObject, mbd);
跟着源码进入到`
AsyncAnnotationBeanPostProcessor
处理的时候,返现返回了一个CgLib代理对象,这时候,剩下的逻辑就一目了然了,实例化后的bean和注入给其他bean不一致,这时候就有问题了,spring认为应该是注入给其他bean的引用应当是一致的,前面放到二级缓存中了,其他bean创建的时候用的就是原始的引用,你初始化后返回了一个代理的引用,spring也就抛出了异常。也算是@Async`的一个小bug。
【「Spring源码」循环依赖解析&遇到的bug分析】分析完毕。也没啥好总结的,都是很简单的流程。觉得分析的风格还行的可以个一个赞

    推荐阅读