今天我們來探討一個有意思的spring原始碼問題,也是一個學生告訴了我現象我從原始碼裡面找到了這個有意思的問題。 首先我們看service層的程式碼案例,如下: @Service("transationServiceImpl") public class TransationServiceImpl implements TransationService { @Autowired TransationService transationService; @Transactional @Async @Override public void transation() { } } 在transation方法上面加上了@Transactional和@Async兩個註解,然後在TransationServiceImpl 類中自己把自己的例項注入到transationService屬性中,存在迴圈依賴,理論上單例的迴圈依賴是允許的。但是我們啟動容器會報錯,測試程式碼如下: public class MyTest { @Test public void test1() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ComponentScanBean.class); } } @Component @ComponentScan(basePackages = {"com.xiangxue"}) public class ComponentScanBean { } 然後右鍵執行test1單元測試載入spring容器就會報錯,報錯資訊如下: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 『transationServiceImpl': Bean with name 『transationServiceImpl' has been injected into other beans [transationServiceImpl] 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. 從報錯的字面意思來看,是存在了多版本的迴圈依賴,如果要解決這個問題,我們必須追溯到原始碼中。 首先我們從TransationServiceImpl 例項化開始講起。 例項化從getBean方法看起,前面程式碼我就不貼了,這篇文章是給讀過spring原始碼的人看的,沒讀過也看不懂,哈哈 。 1、首先第一次建立TransationServiceImpl例項的時候會從快取中獲取例項 ,如果快取裡面有例項則直接返回,第一次建立的時候快取中是沒有例項的,所以會走到else程式碼塊中。 這裡是從三個快取中獲取例項化的詳細程式碼。後面會分析 @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //根據beanName從快取中拿例項 //先從一級快取拿 Object singletonObject = this.singletonObjects.get(beanName); //如果bean還正在建立,還沒建立完成,其實就是堆記憶體有了,屬性還沒有DI依賴注入 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { //從二級快取中拿 singletonObject = this.earlySingletonObjects.get(beanName); //如果還拿不到,並且允許bean提前暴露 if (singletonObject == null && allowEarlyReference) { //從三級快取中拿到物件工廠 ObjectFactory
singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //從工廠中拿到物件 singletonObject = singletonFactory.getObject(); //升級到二級快取 System.out.println("======get instance from 3 level cache->beanName->" + beanName + "->value->" + singletonObject ); this.earlySingletonObjects.put(beanName, singletonObject); //刪除三級快取 this.singletonFactories.remove(beanName); } } } } return singletonObject; } 2、第一次進來快取中沒有則建立TransationServiceImpl的例項 最終會走到doCreateBean方法中進行例項化,部分程式碼如下 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ............非關鍵程式碼不貼了 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否 單例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"); } //這裡著重理解,對理解迴圈依賴幫助非常大,重要程度 5 新增三級快取 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //ioc di,依賴注入的核心方法,該方法必須看,重要程度:5 populateBean(beanName, mbd, instanceWrapper); //bean 例項化+ioc依賴注入完以後的呼叫,非常重要,重要程度:5 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)) { String[] dependentBeans = getDependentBeans(beanName); Set
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."); } } } } ............非關鍵程式碼不貼了 return exposedObject; } 由於業務類有迴圈依賴 所以在第一次例項化業務類的時候,在populateBean(beanName, mbd, instanceWrapper);進行依賴注入時會觸發TransationServiceImpl業務類的getBean操作,也就是會呼叫TransationServiceImpl業務類的getBean方法,第二次會走到TransationServiceImpl例項化的邏輯中。這裡明白的刷朵鮮花敲個1,哈哈。 但是在觸發第二次業務類的getBean操作之前,還有一個非常重要的步驟,就是業務類的提前暴露,也就是三級快取的建立。這塊會建立業務類和ObjectFactory的對映關係這個建立對映關係是在依賴注入之前!!!! 3、迴圈依賴注入觸發TransationServiceImpl類的第二次getBean獲取例項化的邏輯 第二次進來的時候,由於第一次例項化的時候在三級快取中建立了對映關係,所以第二次會從快取中獲取例項 ObjectFactory物件的getObject方法就會呼叫到。getEarlyBeanReference方法,這個方法是會從BeanPostProcessor中獲取例項,這裡可能就會返回代理例項 三級快取的getObject方法會呼叫到getEarlyBeanReference中,斷點一下,看看。 從斷點看, 3:是獲取事務代理的BeanPostProcessor型別是SmartInstantiationAwareBeanPostProcessor型別的,所以事務代理的BeanPostProcessor會進來,然後生成代理 4:是獲取@Async非同步代理的BeanPostProcessor,但是不是SmartInstantiationAwareBeanPostProcessor型別的,所以這裡if就不會進來,所以最後這裡從三級快取中拿到的是事務切面的程式碼物件,注意這裡是類中的依賴注入的例項是事務切面的代理例項,如圖: 可以看到,這裡的advisors切面容器明顯是一個事務切面,所以業務類中依賴注入的是一個事務切面的代理例項。 但是在這裡我還是要說一下,在生成事務代理的時候其實是有做快取的,如下程式碼: 這裡的cacheKey就是TransationServiceImpl業務類的bean的名稱的字串,然後會把這個字串加入到一個earlyProxyReferences的Set容器中 在這裡已經在TransationServiceImpl的第二次getBean的時候從三級快取中獲取到了代理物件了,那麼第二次的例項化已經完成了,並且已經依賴注入到了TransationServiceImpl的屬性中了,這時候依賴注入已經完成了,好,我們還是接著第一次TransationServiceImpl的例項來講,貼程式碼: protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { ............非關鍵程式碼不貼了 // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否 單例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"); } //這裡著重理解,對理解迴圈依賴幫助非常大,重要程度 5 新增三級快取 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //ioc di,依賴注入的核心方法,該方法必須看,重要程度:5 populateBean(beanName, mbd, instanceWrapper); //bean 例項化+ioc依賴注入完以後的呼叫,非常重要,重要程度:5 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)) { String[] dependentBeans = getDependentBeans(beanName); SetactualDependentBeans = 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."); } } } } ............非關鍵程式碼不貼了 return exposedObject; } 也就是populateBean(beanName, mbd, instanceWrapper);依賴注入已經完成了,程式碼接著往下走。 代理會執行到: //bean 例項化+ioc依賴注入完以後的呼叫,非常重要,重要程度:5 exposedObject = initializeBean(beanName, exposedObject, mbd); 在這裡,業務類會在這個方法裡面再次生成代理,這裡就有意思了。程式碼如下 protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction