1. 程式人生 > >spring原始碼閱讀筆記06:bean載入之準備建立bean

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的建立過程了,這個才是真正爬坡的開