Spring IOC源碼解析-創建單例bean的過程

一.簡介

對於實例化好的單例bean,getBean(String)方法並不會再一次去創建,而是從緩存中獲取。如果某個bean還未實例化,這個時候就無法命中緩存,此時,就要根據bean的配置信息去創建這個bean了。相較於getBean(String)方法的實現邏輯,創建bean的方法create(String,RootBeanDefinition,Object[])及所調用的方法邏輯更加複雜一些,本次就先從大體上分析createBean方法邏輯。

二.源碼分析

2.1 創建bean實例的入口

我們首先要知道createBean方法在哪些被調用,如下:

public T doGetBean(...) {
    // 省略不相關代碼
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                try {
                    return createBean(beanName, mbd, args);
                }
                catch (BeansException ex) {
                    destroySingleton(beanName);
                    throw ex;
                }
            }
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    // 省略不相關代碼
}

上面是doGetBean方法的代碼片段,從中可以發現調用了createBean方法。createBean方法被匿名工廠類的getObject方法包裹,但是這個匿名工廠類並未直接調用getObject方法,而是將自身作爲參數傳遞給getSingleton(String,ObjectFactory)方法。getSingleton這個方法實現我們之前講過,邏輯如下:

1.先從singletonObjects集合獲取bean實例,若不爲空,直接返回

2.若爲空,進入創建bean實例階段。先將beanName添加到singletonCurrentlyInCreation

3.通過getObject方法調用createBean方法創建bean實例

4.將beanName從singletonCurrentlyInCreation集合中移除

5.將<beanName,singletonObject>映射緩存到singletonObjects集合中

從上面的分析中,瞭解可createBean在什麼時候和地方被調用,下來需要了解createBean做了什麼事情。

2.2 createBean

源碼分析如下:

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;

    // 解析 bean 的類型
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }

    try {
        // 處理 lookup-method 和 replace-method 配置,Spring 將這兩個配置統稱爲 override method
        mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                beanName, "Validation of method overrides failed", ex);
    }

    try {
        // 在 bean 初始化前應用後置處理,如果後置處理返回的 bean 不爲空,則直接返回
        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);
    }

    // 調用 doCreateBean 創建 bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
        logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
}

執行流程如下:

1.根據設置的class屬性或者根據className來解析class

2.對override屬性進行標記及驗證。在Spring配置中是存着lookup-method和replace-method的,而這兩個配置的加載就是講配置同一存放在BeanDefinition中的methodOverrides屬性裏,而這個函數的操作其實也就是針對這兩個配置的。

3.應用初始化前的後處理器,解析指定bean是否存在初始化的短路操作。

4.創建bean。

2.3 驗證和準備override方法

在Spring配置中是存着lookup-method和replace-method的,而這兩個配置的加載就是講配置同一存放在BeanDefinition中的methodOverrides屬性裏,這兩個功能實現原理其實是在bean實例化的時候如果檢測到methodOverrides屬性裏,會動態地爲當前bean生成代理並使用對應的攔截器對bean做增強處理,相關邏輯實現在bean實例化會講到。

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    MethodOverrides methodOverrides = getMethodOverrides();
    if (!methodOverrides.isEmpty()) {
        Set<MethodOverride> overrides = methodOverrides.getOverrides();
        synchronized (overrides) {
            // 循環處理每個 MethodOverride 對象
            for (MethodOverride mo : overrides) {
                prepareMethodOverride(mo);
            }
        }
    }
}

protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
    // 獲取方法名爲 mo.getMethodName() 的方法數量,當方法重載時,count 的值就會大於1
    int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
    // count = 0,表明根據方法名未找到相應的方法,此時拋出異常
    if (count == 0) {
        throw new BeanDefinitionValidationException(
                "Invalid method override: no method with name '" + mo.getMethodName() +
                "' on class [" + getBeanClassName() + "]");
    }
    // 若 count = 1,表明僅存在已方法名爲 mo.getMethodName(),這意味着方法不存在重載
    else if (count == 1) {
        // 方法不存在重載,則將 overloaded 成員變量設爲 false
        mo.setOverloaded(false);
    }
}

上面的源碼中,prepareMethodOverrides方法循環調用了prepareMethodOverride 方法,並沒其他的太多邏輯。主要準備工作都是在 prepareMethodOverride 方法中進行的。prepareMethodOverride 這個方法主要用於獲取指定方法的方法數量 count,並根據 count 的值進行相應的處理。count = 0 時,表明方法不存在,此時拋出異常。count = 1 時,設置 MethodOverride 對象的overloaded成員變量爲 false。這樣做的目的在於,提前標註名稱mo.getMethodName的方法不存在重載,在使用 CGLIB 增強階段就不需要進行校驗,直接找到某個方法進行增強即可。

2.4 實例化的前置處理

用戶通過實現 BeanPostProcessor 接口,並將實現類配置到 Spring 的配置文件中(或者使用註解),即可在 bean 初始化前後進行自定義操作。

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    // 檢測是否解析過,mbd.beforeInstantiationResolved 的值在下面的代碼中會被設置
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 應用前置處理
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    // 應用後置處理
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        // 設置 mbd.beforeInstantiationResolved
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

2.4.1 實例化前的後處理器應用

bean的實例化前調用,也就是將AbstractBeanDefinition轉換爲BeanWrapper前的處理。給子類一個修改BeanDefinition機會,也就是說當程序經過這個方法後,bean可能不是我們認爲的bean了,而是或許稱爲了一個經過處理代理的bean,也可能是通過cglib生成的也可能是通過其他技術生成的。

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        // InstantiationAwareBeanPostProcessor 一般在 Spring 框架內部使用,不建議用戶直接使用
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // bean 初始化前置處理
            Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
            if (result != null) {
                return result;
            }
        }
    }
    return null;
}

2.4.2 實例化後的後處理器應用

Spring中的規則是在bean的初始化後儘可能保證將註冊的後處理器的postProcessAfterInitialization方法應用到該bean中,因爲如果返回的bean不爲空,那麼便不會再次經歷普通bean的創建過程,所以只能在這裏應用後置處理器postProcessAfterInitialization。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
       // bean 初始化後置處理
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}

2.5 調用 doCreateBean 方法創建 bean

當經歷過後resolveBeforeInstantiation方法之後,程序有兩個選擇,如果創建了代理或者重寫了InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法並在方法的postBeforeInstantiation中改變了bean,則直接返回就行,否則就要驚醒常規bean的創建,常規bean的創建就是doCreateBean中完成的。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
        throws BeanCreationException {

    /* 
     * BeanWrapper 是一個基礎接口,由接口名可看出這個接口的實現類用於包裹 bean 實例。
     * 通過 BeanWrapper 的實現類可以方便的設置/獲取 bean 實例的屬性
     */
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        // 從緩存中獲取 BeanWrapper,並清理相關記錄
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        /* 
         * 創建 bean 實例,並將實例包裹在 BeanWrapper 實現類對象中返回。createBeanInstance 
         * 中包含三種創建 bean 實例的方式:
         *   1. 通過工廠方法創建 bean 實例
         *   2. 通過構造方法自動注入(autowire by constructor)的方式創建 bean 實例
         *   3. 通過無參構造方法方法創建 bean 實例
         *
         * 若 bean 的配置信息中配置了 lookup-method 和 replace-method,則會使用 CGLIB 
         * 增強 bean 實例。後面會解析到
         */
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // 此處的 bean 可以認爲是一個原始的 bean 實例,暫未填充屬性
    final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
    Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
    mbd.resolvedTargetType = beanType;

    // 這裏又遇到後置處理了,此處的後置處理是用於處理已“合併的 BeanDefinition”。
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }

    /*
     * earlySingletonExposure 是一個重要的變量,這裏要說明一下。該變量用於表示是否提前暴露
     * 單例 bean,用於解決循環依賴。earlySingletonExposure 由三個條件綜合而成,如下:
     *   條件1:mbd.isSingleton() - 表示 bean 是否是單例類型
     *   條件2:allowCircularReferences - 是否允許循環依賴
     *   條件3:isSingletonCurrentlyInCreation(beanName) - 當前 bean 是否處於創建的狀態中
     * 
     * earlySingletonExposure = 條件1 && 條件2 && 條件3 
     *                        = 單例 && 是否允許循環依賴 && 是否存於創建狀態中。
     */
    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");
        }
        // 添加工廠對象到 singletonFactories 緩存中
        addSingletonFactory(beanName, new ObjectFactory<Object>() {
            @Override
            public Object getObject() throws BeansException {
                // 獲取早期 bean 的引用,如果 bean 中的方法被 AOP 切點所匹配到,此時 AOP 相關邏輯會介入
                return getEarlyBeanReference(beanName, mbd, bean);
            }
        });
    }

    Object exposedObject = bean;
    try {
        // 向 bean 實例中填充屬性,populateBean 方法也是一個很重要的方法,後面會專門寫文章分析
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            /*
             * 進行餘下的初始化工作,詳細如下:
             * 1. 判斷 bean 是否實現了 BeanNameAware、BeanFactoryAware、
             *    BeanClassLoaderAware 等接口,並執行接口方法
             * 2. 應用 bean 初始化前置操作
             * 3. 如果 bean 實現了 InitializingBean 接口,則執行 afterPropertiesSet 
             *    方法。如果用戶配置了 init-method,則調用相關方法執行自定義初始化邏輯
             * 4. 應用 bean 初始化後置操作
             * 
             * 另外,AOP 相關邏輯也會在該方法中織入切面邏輯,此時的 exposedObject 就變成了
             * 一個代理對象了
             */
            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);
        if (earlySingletonReference != null) {
            // 若 initializeBean 方法未改變 exposedObject 的引用,則此處的條件爲 true。
            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);
                    }
                }
                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.");
                }
            }
        }
    }

    try {
        // 註冊銷燬邏輯
        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 後置處理器相關邏輯

4.根據條件決定是否提前暴露 bean 的早期引用(early reference),用於處理循環依賴問題

5.屬性填充,將所有屬性填充到bean的實例中

6.循環依賴檢查,在這個步驟裏會檢測已經加載的bean是否已經出現了依賴循環,並判斷是否需要拋出異常。

7.註冊DisposableBean,以便於銷燬時候調用

8.完成創建並返回

 

參考:Spring IOC 容器源碼分析 - 創建單例 bean 的過程

           《Spring源碼深度解析》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章