以下內容基于 Spring6.0.4。
關于 Spring 循環依賴,松哥已經連著發了三篇文章了,本篇文章松哥從源碼的角度來和小伙伴們捋一捋 Spring 循環依賴到底是如何解決了。
小伙伴們一定要先熟悉前面文章的內容,否則今天的源碼可能會看起來有些吃力。
接下來我通過一個簡單的循環依賴的案例,來和大家梳理一下完整的 Bean 循環依賴處理流程。
假設我有如下 Bean:
@Servicepublic class A { @Autowired B b;}@Servicepublic class B { @Autowired A a;}
就這樣一個簡單的循環依賴,默認情況下,A 會被先加載,然后在 A 中做屬性填充的時候,去創建了 B,創建 B 的時候又需要 A,就會從緩存中拿到 A,大致流程如此,接下來我們結合源碼來驗證一下這個流程。
首先我們來看獲取 Bean 的時候,如何利用這三級緩存。
小伙伴們知道,獲取 Bean 涉及到的就是 getBean 方法,像我們上面這個案例,由于都是單例的形式,所以 Bean 的初始化其實在容器創建的時候就完成了。
圖片
在 preInstantiateSingletons 方法中,又調用到 AbstractBeanFactory#getBean 方法,進而調用到 AbstractBeanFactory#doGetBean 方法。
圖片
Bean 的初始化就是從這里開始的,我們就從這里來開始看起吧。
AbstractBeanFactory#doGetBean(方法較長,節選部分關鍵內容):
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = transformedBeanName(name); Object beanInstance; // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory abf) { return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } if (!typeCheckOnly) { markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate") .tag("beanName", name); try { if (requiredType != null) { beanCreation.tag("beanType", requiredType::toString); } RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. if (mbd.isSingleton()) { 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; } }); beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } } } return adaptBeanInstance(name, beanInstance, requiredType);}
這個方法比較長,我來和大家說幾個關鍵的點:
以上就是 doGetBean 方法中幾個比較重要的點。
其中有兩個方法我們需要展開講一下,第一個方法就是去三級緩存中查詢 Bean 的 getSingleton 方法(步驟一),第二個方法則是去獲取到 Bean 實例的 getSingleton 方法(步驟六),這是兩個重載方法。
接下來我們就來分析一下這兩個方法。
DefaultSingletonBeanRegistry#getSingleton:
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;}
當我們第一次創建 A 對象的時候,很顯然三級緩存中都不可能有數據,所以這個方法最終返回 null。
接下來看 2.1 小節步驟六的獲取 Bean 的方法。
DefaultSingletonBeanRegistry#getSingleton(方法較長,節選部分關鍵內容):
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); newSingleton = true; } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; }}
我們來看下 addSingleton 方法:
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); }}
小伙伴們看一下,一級緩存中存入 Bean,二級緩存和三級緩存移除該 Bean,同時在 registeredSingletons 集合中記錄一下當前 Bean 已經創建。
所以現在的重點其實又回到了 createBean 方法了。
createBean 方法其實就到了 Bean 的創建流程了。bean 的創建流程在前面幾篇 Spring 源碼相關的文章中也都有所涉獵,所以今天我就光說一些跟本文主題相關的幾個點。
createBean 方法最終會調用到 AbstractAutowireCapableBeanFactory#doCreateBean 方法,這個方法也是比較長的,而我是關心如下幾個地方:
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // 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) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } return exposedObject;}
這里我比較在意的有兩個地方,一個是調用 addSingletonFactory 方法向三級緩存中添加回調函數,回調函數是 getEarlyBeanReference,如果有需要,將來會通過這個回調提前進行 AOP,即使沒有 AOP,就是普通的循環依賴,三級緩存也是會被調用的,這個大家繼續往后看就知道了,另外還有一個比較重要的地方,在本方法一開始的時候,就已經創建出來 A 對象了,這個時候的 A 對象是一個原始 Bean,即單純的只是通過反射把對象創建出來了,Bean 還沒有經歷過完整的生命周期,這里 getEarlyBeanReference 方法的第三個參數就是該 Bean,這個也非常重要,牢記,后面會用到。
第二個地方就是 populateBean 方法,當執行到這個方法的時候,A 對象已經創建出來了,這個方法是給 A 對象填充屬性用的,因為接下來要注入 B 對象,就在這個方法中完成的。
由于我們第 1 小節是通過 @Autowired 來注入 Bean 的,所以現在在 populateBean 方法也主要是處理 @Autowired 注入的情況,那么這個松哥之前寫過文章,小伙伴們參考@Autowired 到底是怎么把變量注入進來的?,具體的注入細節我這里就不重復了,單說在注入的過程中,會經過一個 DefaultListableBeanFactory#doResolveDependency 方法,這個方法就是用來解析 B 對象的(至于如何到達 doResolveDependency 方法的,小伙伴們參考 @Autowired 到底是怎么把變量注入進來的?一文)。
doResolveDependency 方法也是比較長,我這里貼出來和本文相關的幾個關鍵地方:
@Nullablepublic Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { //... Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } //...}
而 descriptor.resolveCandidate 方法又開啟了新一輪的 Bean 初始化,只不過這次初始化的 B 對象,如下:
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException { return beanFactory.getBean(beanName);}
后續流程其實就是上面的步驟,我就直接來跟大家說一說,就不貼代碼了。
現在系統調用 beanFactory.getBean 方法去查找 B 對象,結果又是走一遍本文第二小節的所有流程,當 B 創建出來之后,也要去做屬性填充,此時需要在 B 中注入 A,那么又來到本文的 2.4 小節,最終又是調用到 resolveCandidate 方法去獲取 A 對象。
此時,在獲取 A 對象的過程中,又會調用到 doGetBean 這個方法,在這個方法中調用 getSingleton 的時候(2.1 小節的第一步),這個時候的執行邏輯就跟前面不一樣了,我們再來看下這個方法的源碼:
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;}
現在還是嘗試從三級緩存中獲取 A,此時一二級緩存中還是沒有 A,但是三級緩存中有一個回調函數,當執行 singletonFactory.getObject() 方法的時候,就會觸發該回調函數,這個回調函數就是我們前面 2.4 小節提到的 getEarlyBeanReference 方法,我們現在來看下這個方法:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject;}
這個方法有一個參數 Bean,這個參數 Bean 會經過一些后置處理器處理之后返回,后置處理器主要是看一下這個 Bean 是否需要 AOP,如果需要就進行 AOP 處理,如果不需要,直接就把這個參數 Bean 返回就行了。至于這個參數是哪來的,我在 2.4 小節中已經加黑標記出來了,這個參數 Bean 其實就是原始的 A 對象!
好了,現在 B 對象就從緩存池中拿到了原始的 A 對象,B 對象屬性注入完畢,對象創建成功,進而導致 A 對象也創建成功。
大功告成。
老實說,如果小伙伴們認認真真看過松哥最近發的 Spring 源碼文章,今天的內容很好懂~至此,Spring 循環依賴,從思路到源碼,都和大家分析完畢了~感興趣的小伙伴可以 DEBUG 走一遍哦~
本文鏈接:http://www.tebozhan.com/showinfo-26-5097-0.html透過源碼,捋清楚循環依賴到底是如何解決的!
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 上下文1.6萬token的編程大模型來了!與Stable Diffusion出自同門,一次吃5個Python文件不費勁