建立bean 準備建立bean
前言
上一篇文章講述了迴圈依賴以及Spring中的迴圈依賴的處理方式後,我們接著準備建立bean這篇文章來繼續講述bean的建立,先回顧一下(擷取部分createBean程式碼):
try { // 給BeanPostProcessors一個機會來返回代理來替代真正的例項 Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } }catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; }
可以看出在經歷過resolveBeforeInstantiation方法後,上述程式碼有兩個返回途徑,如果建立了代理或者說重寫了InstantiationAwareBeanPostProcessor類的postProcessBeforeInstantiation方法並在方法postProcessBeforeInstantiation中改變了bean,則直接返回,否則的話就需要進行常規的bean建立。而這種常規的bean建立是在doCreateBean()方法中完成的。
建立bean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 例項化bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //如果是單例的話,移除快取中的bean instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //根據指定的bean使用對應的策略建立新的例項,如:工廠方法,建構函式自動注入,簡單初始化 instanceWrapper = createBeanInstance(beanName, mbd, args); } final 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 { //bean合併後處理 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } //是否需要提早曝光:單例 && 允許迴圈依賴 && 當前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"); } //為了避免後期迴圈依賴,可以在bean初始化完成前將建立例項的ObjectFactory加入工廠 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //對bean進行填充,將各個屬性注入,其中,可能存在依賴於其他bean的屬性,則會遞迴初始化依賴的bean populateBean(beanName, mbd, instanceWrapper); //呼叫初始化方法,比如init-method 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); //earlySingletonReference 只有在檢測到有迴圈依賴的情況下才不會為空 if (earlySingletonReference != null) { //判斷exposedObject是否在初始化方法中被改變 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."); } } } } // Register bean as disposable. try { //根據scope註冊bean registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
日誌和異常的內容非常重要,但是在閱讀原始碼的時候大部分的人都會直接忽略掉。在這裡不探討日誌和異常,我們來看看函式的思路概要(配合註釋其實已經大部分能看懂,先大致瞭解流程步驟,後面我們在一步一步詳細瞭解):
(1)如果是單例則需要首先清除快取;
(2)例項化bean,將BeanDefinition轉換為BeanWrapper;
轉換是一個複雜的過程,大致的功能有:
❤ 如果存在工廠方法則使用工廠方法進行初始化;
❤ 一個類有多個建構函式,每個建構函式有不同的引數,所以需要根據引數鎖定建構函式並進行初始化;
❤ 如果即不存在工廠方法也不存在帶有引數的建構函式,則使用預設的建構函式進行bean的例項化;
(3)bean的合併後處理;
bean的合併後處理,Autowire註解正是通過這個方法實現諸如型別的預解析;
(4)依賴處理;
在Spring中會有迴圈依賴的情況,例如:當A中含有B的屬性,而B中又含有A的屬性就會構成一個迴圈依賴,此時如果A和B都是單例,那麼在Spring中的處理方式就是當建立B的時候,涉及到注入A的操作時,並不是直接去再次建立A,而是通過放入快取中的ObjectFactory來建立例項,這樣就解決了迴圈依賴的問題;
(5)屬性填充,將所有的屬性填充至bean的例項中;
(6)迴圈依賴檢查;
上一篇文章講述了,在Spring中解決迴圈依賴只是對單例有效,而對於property的bean,Spring沒有好的解決辦法,唯一做的就是丟擲異常。在這個步驟裡面會檢測載入的bean是否出現了迴圈依賴,並判斷是否丟擲異常;
(7)註冊DisposableBean;
如果配置了destroy-method,這裡需要註冊以便於在銷燬時呼叫;
(8)完成建立並返回。
可以看出上述的步驟非常的繁瑣,每一步都使用了大量的程式碼來完成其涉及的功能,最複雜也是最難理解的當屬迴圈依賴的處理。下面我們就一步一步詳細的瞭解建立bean的每個步驟。
1 建立bean的例項
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 解析class Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } //如果工廠方法不為空則使用工廠方法初始化策略 if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { //一個類有多個建構函式,每個建構函式都有不同的引數,所以呼叫前需要根據引數鎖定建構函式或者對應的工廠方法 if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } //如果已經解析過則使用解析好的建構函式方法不需要再次鎖定 if (resolved) { if (autowireNecessary) { //建構函式自動注入 return autowireConstructor(beanName, mbd, null, null); } else { //使用預設建構函式自動注入 return instantiateBean(beanName, mbd); } } // 根據引數解析建構函式 Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // 建構函式自動注入 ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // 預設建構函式構造 return instantiateBean(beanName, mbd); }
配合註釋我們可以清晰的看出例項化的邏輯:
(1)如果在RootBeanDefinition中存在factoryMethodName屬性,或者說在配置檔案中配置了factory-method,那麼Spring會嘗試使用InstantiateUsingFactoryMethod(beanName,mbd,args)方法根據RootBeanDefinition中的配置生成bean的例項。
(2)解析建構函式並進行建構函式的例項化。因為一個bean對應的類中可能會有多個建構函式,而每個建構函式的引數不同,Spring根據引數以及型別去判斷最終會使用到哪個建構函式進行例項化。但是,判斷的過程是個比較耗費效能的操作,所以採用了快取機制,如果已經解析過,則不需要重複解析而是直接從RootBeanDefinition中的屬性resolvedConstrutorOrFactoryMethod快取的值去取,否則需要再次解析,並將解析的結果新增至RootBeanDefinition中的屬性resolvedConstructorOrFactoryMethod中。
接下來,對createBeanInstance方法中的重點方法進行解析:
1.autowiredConstructor
對於例項的建立Spring中分成了兩種情況,一種是通用的例項化,另一種是帶有引數的例項化。帶有引數的例項化過程相當複雜,因為存在著不確定性,所以在判斷對應引數上做了大量的工作:
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor<?> constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; //exolicitArgs通過getBean傳入,如果getBean方法呼叫的時候指定方法引數那麼直接使用 if (explicitArgs != null) { argsToUse = explicitArgs; } else { //如果在getBean方法的時候沒有指定則嘗試從配置檔案中解析 Object[] argsToResolve = null; //嘗試從快取中獲取 synchronized (mbd.constructorArgumentLock) { constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // 從快取中去取 argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { //配置建構函式引數 argsToResolve = mbd.preparedConstructorArguments; } } } //如果快取中存在 if (argsToResolve != null) { //解析引數型別,如給定方法的建構函式A(int,int)則通過此方法後就會把配置中的("1","1")轉換為(1,1) argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true); } } //沒有被快取 if (constructorToUse == null || argsToUse == null) { // Take specified constructors, if any. Constructor<?>[] candidates = chosenCtors; if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); try { candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } } if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { Constructor<?> uniqueCandidate = candidates[0]; if (uniqueCandidate.getParameterCount() == 0) { synchronized (mbd.constructorArgumentLock) { mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; mbd.constructorArgumentsResolved = true; mbd.resolvedConstructorArguments = EMPTY_ARGS; } bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS)); return bw; } } // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { //提取配置檔案中的配置的建構函式引數 ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); //用於承載解析後的建構函式引數的值 resolvedValues = new ConstructorArgumentValues(); //能解析到的引數個數 minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) { // 已經找到滿足的構造引數,就返回 break; } if (paramTypes.length < minNrOfArgs) { //引數個數不相等 continue; } ArgumentsHolder argsHolder; if (resolvedValues != null) { //有引數則根據值構造對應引數型別的引數 try { //註釋上獲取引數名稱 String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { //獲取引數名稱探索器 ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { //獲取指定建構函式的引數名稱 paramNames = pnd.getParameterNames(candidate); } } //根據引數和資料型別建立引數持有者 argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1); } catch (UnsatisfiedDependencyException ex) { if (logger.isTraceEnabled()) { logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex); } // Swallow and try next constructor. if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } else { //給定的顯式引數->引數長度必須完全匹配。 if (paramTypes.length != explicitArgs.length) { continue; } //建構函式沒有引數的情況 argsHolder = new ArgumentsHolder(explicitArgs); } //探測是否有不確定的建構函式存在,例如不同建構函式的引數為父子關係 int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // 如果表示最接近的匹配,則選擇此建構函式。 if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } if (constructorToUse == null) { if (causes != null) { UnsatisfiedDependencyException ex = causes.removeLast(); for (Exception cause : causes) { this.beanFactory.onSuppressedException(cause); } throw ex; } throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Could not resolve matching constructor " + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)"); } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Ambiguous constructor matches found in bean '" + beanName + "' " + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " + ambiguousConstructors); } if (explicitArgs == null && argsHolderToUse != null) { //將解析的建構函式加入快取 argsHolderToUse.storeCache(mbd, constructorToUse); } } Assert.state(argsToUse != null, "Unresolved constructor arguments"); //將構建的例項加入到BeanWrapper中 bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); return bw; }autowiredConstructor
邏輯很複雜,函式的程式碼量很大。我們來總覽一下整個函式:
(1)建構函式引數的確定;
1.根據explicitArgs引數判斷。
如果傳入的引數explicitArgs不為空,那邊可以直接確定引數,因為explicitArgs引數是在呼叫bean的時候使用者指定的,在BeanFactory類中存在這樣的方法:
Object getBean(String name, Object... args) throws BeansException;
在獲取bean的時候,使用者不但可以指定bean的名稱還可以指定bean所對應類的建構函式或者工廠方法的方法引數,主要用於靜態工廠方法的呼叫,而這裡是需要給定完全匹配的引數的,所以,便可以判斷,如果傳入引數explicitArgs不為空,則可以確定建構函式引數就是它。
2.快取中獲取。
除此之外,確定引數的辦法如果之前已經分析過,也就是說建構函式引數已經記錄在快取中,那麼便可以直接拿來使用。而且,這裡要提到的是,在快取中快取的可能是引數的最終型別也可能是引數的初始型別,例如:建構函式引數要求的是int型別,但是原始的引數值可能是String型別的“1”,那麼即使在快取中得到了引數,也需要經過型別轉換器的過濾以確保引數型別與對應的建構函式引數型別完全對應。
3.配置檔案獲取。
如果不能根據傳入的引數explicitArgs確定建構函式的引數也無法在快取中得到相關的資訊,那麼就只能開始新一輪的分析了。
分析從獲取配置檔案中的配置的建構函式資訊開始,經過之前的分析,我們知道,Spring中配置檔案中的資訊經過轉換都會通過BeanDefinition例項來承載,也就是引數mbd中包含,那麼可以通過呼叫mbd.getConstrutorArgumentValues()來獲取配置的建構函式資訊。有了配置中的資訊便可以獲取對應的引數值資訊了,獲取引數值的資訊包括直接指定值,如:直接指定建構函式中的某個值為原始型別String型別,或者是一個對其他bean的引用,而這一處理委託給resolveConstructorArguments方法,並返回能解析到的引數的個數。
(2)建構函式的確定。
經過了第一步之後已經確定了建構函式的引數,接下來的任務就是根據建構函式引數在所有建構函式中鎖定對應的建構函式,而匹配的方法就是根據引數個數匹配,所以在匹配之前需要先對建構函式按照public建構函式優先引數數量降序。這樣可以在遍歷的情況下迅速判斷排在後面的建構函式引數個數是否符合條件。
由於在配置檔案中並不是唯一限制使用引數位置索引的方式去建立,同樣還支援指定引數名稱進行設定引數值的情況,如<constructor-arg name="a"> ,那麼這種情況下就需要首先確定建構函式中的引數名稱。
獲取引數名稱可以有兩種方式,一種是通過註解的方式直接獲取,另一種就是使用Spring中提供的工具類ParameterNameDiscoverer來獲取。建構函式、引數名稱、引數型別、引數值都可以確定後就可以鎖定建構函式以及轉換對應的引數型別了。
(3)根據確定的建構函式轉換對應的引數型別;
主要是使用Spring中提供的型別轉換器或者使用者自定義型別轉換器進行轉換。
(4)建構函式不確定性的驗證;
當然有時候即使建構函式、引數名稱、引數型別、引數值都確定後也不一定會直接鎖定建構函式,不同建構函式的引數為父子關係,所以Spring在最後又做了一次驗證。
(5)根據例項化策略以及得到的建構函式及建構函式引數例項化bean。
2.instantiateBean
相信在瞭解了帶引數的建構函式例項構造,理解不帶引數的建構函式將是非常愉快的過程:
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, parent), getAccessControlContext()); } else { beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
發現,此方法並沒有什麼實質性的邏輯,直接呼叫例項化策略進行例項化就可以了。
3.例項化策略
在上述例項化的過程中反覆的提到過例項化策略,那這又是做什麼用的呢?其實,經過前面的分析,我們已經得到了足以例項化的所有相關的資訊,完全可以使用最簡單的反射方法來構造例項物件,但是Spring卻並沒有這麼做。
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // 如果有需要覆蓋或者動態替換的方法則當然需要使用cglib進行動態代理,因為可以在建立代理的同時將動態方法植入類中,但是如果沒有需要動態改變方法,為了方便直接反射就行了 if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
看了上面的函式後似乎能感受到Spring的用心良苦,為了能更方便的使用Spring而做了大量的工作。
在程式中,首先判斷了BeanDefinition.getMethodOverrides()為空也就是使用者沒有使用replace或者lookup的配置方法,那麼直接使用反射的方式,簡單快捷,但是如果使用了這兩個特性,再直接使用反射的方式就不妥了,因為需要將這兩個配置提供的功能切入進去,所以就必須要使用動態代理的方式將包含兩個特性所對應的邏輯攔截增強器設定進去,這樣才可以保證在呼叫方法的時候會被相應的攔截器增強,返回值為包含攔截器的代理例項。
2 記錄建立bean的ObjectFactory
在doCreateBean函式中有這樣的一段程式碼:
//是否需要提早曝光:單例 && 允許迴圈依賴 && 當前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"); } //為了避免後期迴圈依賴,可以在bean初始化完成前將建立例項的ObjectFactory加入工廠 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
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; }
上述第一段程式碼時doCreateBean方法中的,第二段是getEarlyBeanReference方法的實現。
這段程式碼邏輯其實不復雜,但是卻不是很好理解,我們需要從全域性的角度去思考Spring的依賴解決辦法:
❤ earlySingletonExposure:從字面上的意思理解就是提早曝光的單例,我們暫時不定義它的學名叫什麼,我們感興趣的是有哪些條件影響這個值;
❤ mbd.isSingleton():判斷此RootBeanDefinition代表的是否是單例;
❤ this.allowCircularReferences:是否允許迴圈依賴,此屬性不再配置檔案中配置,是在AbstractRefreshableApplicationContext 中提供了設定函式,可以通過硬編碼的方式進行設定或者可以通過自定義名稱空間進行設定,其中硬編碼的方式如下:
ClassPathXmlApplicationContext bf = new ClassPathXmlApplicationContext("spring.xml"); bf.setAllowBeanDefinitionOverriding(false);
❤ isSingletonCurrentlyCreation(beanName):該bean是否在建立中。在Spring中,會有個專門的屬性預設為DefaultSingletonBeanRegistry 的singletonsCurrentlyInCreation來記錄bean的載入狀態,在bean開始建立前會將beanName記錄在屬性中,在bean建立結束後會將beanName從屬性中移除。那麼我們跟隨程式碼一路走來,其實對這個屬性的記錄並沒有什麼印象,那麼這個狀態是在那裡記錄的呢?不同的scope的記錄位置並不一樣,以singleton為例,在singleton下記錄屬性的函式是在DefaultSingletonBeanRegistry類的getSingleton(String beanName,ObjectFactory singletonFactory)函式的beforeSingletonCreation(beanName)和afterSingletonCreation(beanName)與this.singletonsCurrentlyInCreation.remove(beanName)來進行狀態的記錄與移除。
經過以上的分析我們瞭解了變數earlySingletonExposure是否是單例、是否允許迴圈依賴、是否對應的bean正在建立的條件的綜合。當這三個條件都滿足時會執行addSingletonFactory操作,那麼加入SingletonFactory的作用是什麼呢?又是在什麼時候呼叫呢?
我們以最簡單的AB迴圈依賴為例,類A中有屬性類B,類B中有屬性類A,那麼初始化beanA的過程如下圖所示:
上圖展示了建立beanA的流程,從圖中我們可以看到,在建立A的時候首先會記錄類A所對應的beanName,並將beanA的建立工廠加入到快取中,而在對A的屬性填充也就是呼叫populateBean方法的時候又再一次的對B進行遞迴建立。同樣的,因為在B中存在A的屬性,因此在例項化B的時候在執行populateBean的時候又會再次初始化A,也就是圖形的最後,呼叫getBean(A)。關鍵就是在這裡,在這個函式中並不是至二級例項化A,而是先去快取中檢測是否已經有建立好的對應的bean,或者是否已經建立好的ObjectFactory,而此時對於A的ObjectFactory早就已經建立好了,所以便不會再去執行建立A,而是直接呼叫ObjectFactory去建立A。
這篇文章講述了建立bean的大致所有步驟和對建立bean中的步驟:建立bean的例項和記錄bean的ObjectFactory,下篇文章會繼續講述建立bean的其他步驟。
參考:《Spring原始碼深度解析》 郝佳 編著: