spring原始碼閱讀筆記06:bean載入之準備建立bean
上文中我們學習了bean載入的整個過程,我們知道從spring容器中獲取單例bean時會先從快取嘗試獲取,如果快取中不存在已經載入的單例bean就需要從頭開始bean的建立,而bean的建立過程是非常複雜的,本文就開始研究bean載入這部分的原始碼。
1. bean建立流程分析
在Spring中bean載入的邏輯是在getSingleton的過載方法中實現的:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); // 全域性變數需要同步 synchronized (this.singletonObjects) { // 首先檢查對應的bean是否已經載入過,因為singleton模式就是複用已建立的bean,所以這一步是必須的 Object singletonObject = this.singletonObjects.get(beanName); // 如果為空才可以進行singleton的bean的初始化 if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while the singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } beforeSingletonCreation(beanName); boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<Exception>(); } try { // 初始化bean singletonObject = singletonFactory.getObject(); } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } // 加入快取 addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } }
這裡建立bean使用了回撥方法(其實是匿名內部類),真正獲取單例bean的方法其實現邏輯是在ObjectFactory型別的例項singletonFactory的getObject()方法中實現的。Spring在建立單例前後還有一些準備及處理操作,包括如下內容:
- 檢查快取是否已經載入過;
- 若沒有載入,則記錄beanName為正在載入狀態;
- 載入單例前記錄載入狀態;
- 通過呼叫引數傳入的ObjectFactory的個體Object方法例項化bean;
- 載入單例後的處理方法呼叫;
- 將結果記錄至快取並刪除載入bean過程中所記錄的各種輔助狀態;
- 返回處理結果;
概括起來主要有下面幾方面:
1.1 載入單例前後處理載入狀態
在beforeSingletonCreation()方法中有一個很重要的操作:記錄載入狀態,也就是通過this.singletonsCurrentlyInCreation.add(beanName)將當前正要建立的bean記錄在快取中,這樣便可以對迴圈依賴進行檢測。
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.containsKey(beanName) && this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) { throw new BeanCurrentlyInCreationException(beanName); } }
同記錄載入狀態相似,當bean載入結束後需要移除快取中記錄的該bean的載入狀態記錄:
protected void afterSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.containsKey(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); } }
1.2 記錄載入結果
將結果記錄至快取並刪除載入bean過程中所記錄的各種輔助狀態:
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
1.3 返回處理結果
雖然前面已經分析了載入bean的邏輯架構,但現在並沒有開始對bean載入功能的探索,前面提到過,bean載入邏輯其實是在匿名內部類ObjectFactory的getObject()方法中定義的:
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { 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; } } });
ObjectFactory的核心部分其實只是呼叫了createBean()方法,也就是建立的bean的邏輯都在這裡面,所以我們還需繼續尋找真理。
2. 準備建立bean
跟蹤了這麼多Spring程式碼,也發現了一些規律:一個真正幹活的函式其實是以do開頭的,比如doGetObjectFromFactoryBean(),而容易給我們帶來錯覺的函式,比如getObjectFromFactoryBean(),其實只是從全域性角度做了一些統籌工作。這個規則對於createBean()也不例外,我們就來看一下其中做了哪些準備工作:
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } // 根據設定的class屬性或者根據className來解析Class resolveBeanClass(mbd, beanName); // 驗證及準備覆蓋的方法 try { mbd.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // 給BeanPostProcessors一個機會返回代理的機會來替代真正的例項 Object bean = resolveBeforeInstantiation(beanName, mbd); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } Object beanInstance = doCreateBean(beanName, mbd, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; }
總結一下具體步驟:
- 根據設定的class屬性或者根據className來解析Class;
- 對override屬性進行標記及驗證;
- 應用初始化前的後處理器,解析指定bean是否存在初始化前的短路操作;
- 建立bean;
在Spring的配置裡面是沒有諸如override-method之類的配置,那為什麼還需要有override屬性進行標記及驗證這一步呢?這是因為在Spring配置中存在lookup-method和replace-method的,而這兩個配置的載入其實就是將配置統一存放在BeadDefinition中的methodOverrides屬性裡,而這步操作就是針對這兩個配置的。
來看一下這幾個主要的步驟:
2.1 處理override屬性
這部分的邏輯是在AbstractBeanDefinition類的prepareMethodOverrides方法中:
public void prepareMethodOverrides() throws BeanDefinitionValidationException { // Check that lookup methods exists. MethodOverrides methodOverrides = getMethodOverrides(); if (!methodOverrides.isEmpty()) { for (MethodOverride mo : methodOverrides.getOverrides()) { prepareMethodOverride(mo); } } } protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { // 獲取對應類中對應方法名的個數 int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); if (count == 0) { throw new BeanDefinitionValidationException( "Invalid method override: no method with name '" + mo.getMethodName() + "' on class [" + getBeanClassName() + "]"); } else if (count == 1) { // 標記MethodOverride暫未被覆蓋,避免參數型別檢查的開銷 mo.setOverloaded(false); } }
在Spring配置中存在lookup-method和replace-method兩個配置功能,而這兩個配置的載入其實就是將配置統一存放在BeanDefinition中的methodOverrides屬性裡,這兩個功能實現原理其實是在bean例項化的時候如果檢測到存在methodOverrides屬性,會動態地為當前bean生成代理並使用對應的攔截器為bean做增強處理,這部分在建立bean部分會做詳細解析。
但是這裡要提到的是,對於方法的匹配來講,如果一個類中存在若干個過載方法,那麼,在函式呼叫及增強的時候還需要根據引數型別進行匹配,來最終確認當前呼叫的到底是哪個函式。但是,Spring將一部分匹配工作在這裡完成了,如果當前類中的方法只有一個,那麼就設定該方法沒有被過載,這樣在後續呼叫的時候便可以直接使用找到的方法,而不需要進行方法的引數匹配驗證了,而且還可以提前對方法存在性進行驗證,正可謂一石二鳥,這部分需要結合後面的邏輯來理解,現在不理解可以先忽略。
2.2 例項化的前置處理
在真正呼叫doCreate方法建立bean例項前使用了方法resolveBeforeInstantiation()對BeanDefinition中的屬性做一些前置處理。這裡無論其中是否有相應的邏輯實現,我們都可以理解,因為真正邏輯實現前後留有處理函式也是可擴充套件的一種體現。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
這裡最重要的無疑是兩個方法applyBeanPostProcessorsBeforeInstantiation以及applyBeanPostProcessorsAfterInitialization,其實現非常簡單,無非是對後處理器中所有InstantiationAwareBeanPostProcessor型別的後處理器進行postProcessBeforeInstantiation方法和BeanPostProcessor的postProcessAfterInitialization方法的呼叫。
例項化前的後處理器應用
bean的例項化前呼叫,也就是將AbstractBeanDefinition轉換為BeanWrapper前的處理,這相當於給子類一個修改BeanDefinition的機會,也就是說當程式經過這個方法之後,bean可能已經不是我們認為的bean了,有可能是一個經過處理的代理bean,可能是通過cglib生成也可能是通過其他技術生成的,這個在後面涉及到AOP時會講到,現在我們只需要知道,在bean的例項化前會呼叫後處理器的方法進行處理:
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; }
例項化後的後處理器應用
Spring中的規則是在bean初始化後儘可能保證將註冊的後處理器的postProcessAfterInitialization方法應用到該bean中,因為如果返回的bean不為空,那麼便不會再次經歷普通bean的建立過程,所以只能在這裡應用後處理器的postProcessAfterInitialization方法。
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
這裡還有一點很重要,在該函式中還有一個短路判斷,這個很關鍵:
if(bean != null) { return bean; }
當經過前置處理後返回的結果如果不為空,那麼會直接略過後續的Bean建立而直接返回,這個地方很容易被忽視,但是卻起著至關重要的作用,我們熟知的AOP功能就是基於這裡的判斷的,後面關於AOP的文章中也會涉及到。
3. bean建立
當經歷過resolveBeforeInstantiation()方法後,如果建立了代理或者重寫了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation()方法並在方法postProcessBeforeInstantiation()中改變了bean,則直接返回就可以了,否則就需要進行常規bean的建立,這是在doCreateBean中完成的:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 根據指定bean使用對應的策略建立新的例項,如:工廠方法、建構函式自動注入、簡單初始化 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { // 應用MergedBeanDefinitionPostProcessor applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // 是否需要提早曝光:單例&允許迴圈依賴&當前bean正在建立中,檢測迴圈依賴 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 為避免後期迴圈依賴,可以在bean初始化完成前將建立例項的ObjectFactory加入工廠 addSingletonFactory(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { // 對bean再一次依賴引用,主要應用SmartInstantiationAwareBeanPostProcessor // 其中我們熟知的AOP就是在這裡將advice動態織入bean中,若沒有則直接返回bean,不做任何處理 return getEarlyBeanReference(beanName, mbd, bean); } }); } // Initialize the bean instance. Object exposedObject = bean; try { // 對bean進行填充,將各個屬性值注入,其中,可能存在依賴於其他bean的屬性,則會遞迴初始依賴bean populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { // 呼叫初始化方法,比如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<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { // 檢測依賴 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } // 因為bean建立後其所依賴的bean一定是已經建立的, // actualDependentBeans不為空則表示當前bean建立後其依賴的bean卻沒有全部 // 建立完,也就是說存在迴圈依賴 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. MergedBeanDefinitionPostProcessor的應用。
bean合併後的處理,Autowired註解正是通過此方法實現諸如型別的解析。
4. 依賴處理
在Spring會有迴圈依賴的情況,例如,當A中含有B的屬性,而B中又含有A的屬性時就會構成一個迴圈依賴,此時如果A和B都是單例,那麼在Spring中的處理方式就是當建立B的時候,涉及自動注入A的步驟時,並不是直接去再次建立A,而是通過放入快取中的ObjectFactory來建立例項,這樣就解決了迴圈依賴問題。
5. 屬性填充。將所有的屬性填充至bean的例項中。
6. 迴圈依賴檢查。
Spring中只在單例下才會解決迴圈依賴,而對於prototype的bean,Spring沒有好的解決辦法,唯一要做的就是丟擲異常。在這個步驟裡面會檢測已經載入的bean是否已經出現了依賴迴圈,並判斷是否需要丟擲異常。
7. 註冊DisposableBean
如果配置了destroy-method,這裡需要註冊以便於在銷燬時候呼叫。
8. 完成建立並返回。
4. 總結
本文先從全域性角度分析了bean建立的整個流程,然後著重分析了Spring在bean建立之前所做的一些準備工作,包括override屬性處理、例項化前後對後處理器的應用,這些都只是一些全域性性的統籌工作,之後又看了一下bean建立的實際過程,後面就要開始詳細分析bean的建立過程了,這個才是真正爬坡的開