Spring bean生命週期
可以簡化為以下5步。
1、構建BeanDefinition
2、例項化 Instantiation
3、屬性賦值 Populate
4、初始化 Initialization(BeanPostprocessor -> Aware,init)
5、銷燬 Destruction
Spring 三級快取作用
一級快取
/** Cache of singleton objects: bean name to bean instance. */ Map<String, Object> singletonObjects;
用來儲存例項化、初始化都完成的bean物件。
二級快取
/** Cache of early singleton objects: bean name to bean instance. */ Map<String, Object> earlySingletonObjects ;
用來儲存例項化完成,但是未初始化完成的物件(這個物件不一定是原始物件,也有可能是經過AOP生成的代理物件)。
三級快取
/** Cache of singleton factories: bean name to ObjectFactory. */ Map<String, ObjectFactory<?>> singletonFactories;
用來儲存一個物件工廠(ObjectFactory),提供一個匿名內部類,用於建立二級快取中的物件。
三級快取中提到的ObjectFactory即 () -> getEarlyBeanReference(beanName,mbd,bean),其中bean就是原始物件。
其中getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor介面中定義的,AbstractAutoProxyCreator(Spring AOP proxy creator)實現了該方法。
Spring三級快取實現
獲取beanName:A
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:true)
分別按照一級快取、二級快取、三級快取順序載入。如果存在迴圈依賴(比如beanName:B依賴beanName:A),而且三級快取中存在beanName:A的引用,則從三級快取中拿到beanName:A對應的提早曝光的物件(可能是原始物件,也可能是代理物件)並放入二級快取。比如又有beanName:C依賴beanName:A,會直接從二級快取中獲取到。
bean建立和初始化完成
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, ObjectFactory:lamda表達,呼叫AbstractBeanFactory#createBean(beanName:A))
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton(beanName:A,singletonObject:A)
直接新增到一級快取
bean建立完成之後
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName:A, RootBeanDefinition:mbd, Object[]:args) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(beanName:A, RootBeanDefinition:mbd, Object[]:args) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(beanName:A, () -> getEarlyBeanReference(beanName:A, mbd, bean:A)) //放入三級快取,beanName:A -> ObjectFactory( () -> getEarlyBeanReference(beanName:A, mbd, bean:A) ) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean //屬性填充 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean //初始化
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; 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)) { } } } }
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:false)
一個簡單的A、B互相依賴迴圈依賴場景
@Async註解迴圈依賴報錯
@Transactional使用的是自動代理建立器AbstractAutoProxyCreator,它實現了getEarlyBeanReference()方法從而很好的對迴圈依賴提供了支援。
@Async的代理建立使用的是AsyncAnnotationBeanPostProcessor單獨的後置處理器實現的,它只在一處postProcessAfterInitialization()實現了對代理物件的建立,因此若出現它被迴圈依賴了,就會報BeanCurrentlyInCreationException。
protected Object doCreateBean( ... ){ ... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } ... Object exposedObject = bean; /** *假如A例項有方法注有@Async註解,A例項依賴B例項,B例項依賴A例項。建立A例項時先走到populateBean方法,然後開始填充屬性B例項,B例項也會走到populateBean方法,然後從三級快取中通過A流程中的getEarlyBeanReference()方法,從而拿到A的早期引用*。執行A的getEarlyBeanReference()方法的時候,會執行自動代理建立器,這裡最終得到是可能是原始A物件,也可能是代理後的A物件(注意哦,A例項的@Async註解這裡還沒有被處理呢)。exposedObject此時指向的是原始A例項。 */ populateBean(beanName, mbd, instanceWrapper); /** *標註有@Async的A例項的代理物件在此處會被生成, 參照類:AsyncAnnotationBeanPostProcessor。執行完之後,exposedObject指向的是個代理物件而非原始A例項了。 */ exposedObject = initializeBean(beanName, exposedObject, mbd); ... // 這裡是報錯的重點。 if (earlySingletonExposure) { /** *因為A被B迴圈依賴進去了,所以此時A是被放進了二級快取的,所以此處earlySingletonReference指向的是通過建立A例項流程中的getEarlyBeanReference()返回的A例項(再強呼叫一下,可能是原始物件,也可能是代理物件)。 *說到這裡,什麼情況下earlySingletonReference==null?也就是getSingleton(beanName:A, false)==null,只有當A例項沒有牽涉到迴圈依賴的時候(即不存在A依賴B且B依賴A的場景;單獨存在B依賴A是沒有問題,A的三級快取根本不會執行,所以二級快取就不會有值,A建立並初始化完成之後直接放到了一級快取)。 */ Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //這裡exposedObject指向的是被@Aysnc代理過的物件,而bean是原始物件,所以此處不相等,走else邏輯。 if (exposedObject == bean) { exposedObject = earlySingletonReference; } /** *allowRawInjectionDespiteWrapping 標註是否允許此Bean的原始型別被注入到其它Bean裡面,即使自己最終會被包裝(代理)。 *預設是false表示不允許,如果改為true表示允許,就不會報錯啦。這是我們後面講的決方案的其中一個方案。 *另外dependentBeanMap是記錄著每個Bean它所依賴的Bean的Map。 */ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { //因為A依賴於B,所以此處拿到了B例項的beanName String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); /** *B例項經過removeSingletonIfCreatedForTypeCheckOnly最終返返回false 因為alreadyCreated裡面已經有它了表示B已經完全建立完成了。 *既然B例項已經建立完成了,通過建立A例項流程中的getEarlyBeanReference()返回的A例項已經注入到了B例項中,此時B例項注入的和exposedObject指向的不是同一個A例項,那肯定就有問題了。 */ 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."); } } } } ... }
具體原因分析參考:https://blog.csdn.net/f641385712/article/details/92797058
總結
1、Spring 解決迴圈依賴有兩個前提條件:不全是構造器方式的迴圈依賴,必須是單例。
2、如果沒有出現迴圈依賴,第三級快取(singletonFactories)將不會使用到,物件會按照Spring建立bean的生命週期流程,最後將bean直接放到第一級快取(singletonObjects)中。
3、一定要三級快取嘛,二級快取不能解決迴圈依賴?不能,主要是為了生成代理物件。
因為三級快取中放的是生成具體物件的匿名內部類(ObjectFactory),它可能生成代理物件,也可能是普通的例項物件。使用三級快取主要是為了保證不管什麼時候使用的都是一個物件。假設只有二級快取的情況,往二級快取中放的顯示一個普通的Bean物件,BeanPostProcessor
去生成代理物件之後,覆蓋掉二級快取中的普通Bean物件,無法保證程多執行緒環境下獲取到bean物件一致性。