歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之開啟 bean 的實體化行程

精品專欄

 

原文出自:http://cmsblogs.com

在上篇部落格【死磕 Spring】—– 載入 bean 之 分析各 scope 的 bean 建立中有一個核心方法沒有講到 createBean() ,該方法的如下:

  1. protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

  2.            throws BeanCreationException;

該方法定義在 AbstractBeanFactory 中。其含義是根據給定的 BeanDefinition 和 args實體化一個 bean 物件,如果該 BeanDefinition 存在父類,則該 BeanDefinition 已經合併了父類的屬性。所有 Bean 實體的建立都會委託給該方法實現。

方法接受三個引數:

  • beanName:bean 的名字

  • mbd:已經合併了父類屬性的(如果有的話)BeanDefinition

  • args:用於建構式或者工廠方法建立 bean 實體物件的引數

該抽象方法的預設實現是在類 AbstractAutowireCapableBeanFactory 中實現,如下:

  1.   protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

  2.            throws BeanCreationException {

  3.        if (logger.isDebugEnabled()) {

  4.            logger.debug("Creating instance of bean '" + beanName + "'");

  5.        }

  6.        RootBeanDefinition mbdToUse = mbd;

  7.        // 確保此時的 bean 已經被解析了

  8.        // 如果獲取的class 屬性不為null,則克隆該 BeanDefinition

  9.        // 主要是因為該動態解析的 class 無法儲存到到共享的 BeanDefinition

  10.        Class> resolvedClass = resolveBeanClass(mbd, beanName);

  11.        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {

  12.            mbdToUse = new RootBeanDefinition(mbd);

  13.            mbdToUse.setBeanClass(resolvedClass);

  14.        }

  15.        try {

  16.            // 驗證和準備改寫方法

  17.            mbdToUse.prepareMethodOverrides();

  18.        }

  19.        catch (BeanDefinitionValidationException ex) {

  20.            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),

  21.                    beanName, "Validation of method overrides failed", ex);

  22.        }

  23.        try {

  24.            // 給 BeanPostProcessors 一個機會用來傳回一個代理類而不是真正的類實體

  25.            // AOP 的功能就是基於這個地方

  26.            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

  27.            if (bean != null) {

  28.                return bean;

  29.            }

  30.        }

  31.        catch (Throwable ex) {

  32.            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,

  33.                    "BeanPostProcessor before instantiation of bean failed", ex);

  34.        }

  35.        try {

  36.            // 執行真正建立 bean 的過程

  37.            Object beanInstance = doCreateBean(beanName, mbdToUse, args);

  38.            if (logger.isDebugEnabled()) {

  39.                logger.debug("Finished creating instance of bean '" + beanName + "'");

  40.            }

  41.            return beanInstance;

  42.        }

  43.        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {

  44.            throw ex;

  45.        }

  46.        catch (Throwable ex) {

  47.            throw new BeanCreationException(

  48.                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);

  49.        }

  50.    }

過程如下:

  • 解析指定 BeanDefinition 的 class

  • 處理 override 屬性

  • 實體化的前置處理

  • 建立 bean

解析指定 BeanDefinition 的 class

  1. Class> resolvedClass = resolveBeanClass(mbd, beanName)

這個方法主要是解析 bean definition 的 class 類,並將已經解析的 Class 儲存在 bean definition 中以供後面使用。如果解析的 class 不為空,則會將該 BeanDefinition 進行克隆至 mbdToUse,這樣做的主要目的是以為動態解析的 class 是無法儲存到共享的 BeanDefinition 中。

處理 override 屬性

大家還記得 lookup-method 和 replace-method 這兩個配置功能?在部落格 【死磕 Spring】----- IOC 之解析Bean:解析 bean 標簽(三) 中已經詳細分析了這兩個標簽的用法和解析過程,知道解析過程其實就是講這兩個配置存放在 BeanDefinition 中的 methodOverrides 屬性中,我們知道在 bean 實體化的過程中如果檢測到存在 methodOverrides,則會動態地位為當前 bean 生成代理並使用對應的攔截器為 bean 做增強處理。具體的實現我們後續分析,現在先看 mbdToUse.prepareMethodOverrides() 都幹了些什麼事,如下:

  1.    public void prepareMethodOverrides() throws BeanDefinitionValidationException {

  2.        if (hasMethodOverrides()) {

  3.            Set<MethodOverride> overrides = getMethodOverrides().getOverrides();

  4.            synchronized (overrides) {

  5.                for (MethodOverride mo : overrides) {

  6.                    prepareMethodOverride(mo);

  7.                }

  8.            }

  9.        }

  10.    }

如果存在 methodOverrides 則獲取所有的 override method ,然後透過迭代的方法一次呼叫 prepareMethodOverride() ,如下:

  1.    protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {

  2.        int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());

  3.        if (count == 0) {

  4.            throw new BeanDefinitionValidationException(

  5.                    "Invalid method override: no method with name '" + mo.getMethodName() +

  6.                    "' on class [" + getBeanClassName() + "]");

  7.        }

  8.        else if (count == 1) {

  9.            mo.setOverloaded(false);

  10.        }

  11.    }

根據方法名稱從 class 中獲取該方法名的個數,如果為 0 則丟擲異常,如果 為 1 則設定該多載方法沒有被多載。若一個類中存在多個多載方法,則在方法呼叫的時候還需要根據引數型別來判斷到底多載的是哪個方法。在設定多載的時候其實這裡做了一個小小最佳化,那就是當 count==1 時,設定 overloaded=false,這樣表示該方法沒有多載,這樣在後續呼叫的時候便可以直接找到方法而不需要進行方法引數的校驗。

誠然,其實 mbdToUse.prepareMethodOverrides() 並沒有做什麼實質性的工作,只是對 methodOverrides 屬性做了一些簡單的校驗而已。

實體化的前置處理

resolveBeforeInstantiation() 的作用是給 BeanPostProcessors 後置處理器傳回一個代理物件的機會,其實在呼叫該方法之前 Spring 一直都沒有建立 bean ,那麼這裡傳回一個 bean 的代理類有什麼作用呢?作用體現在後面的 if 判斷:

  1. if (bean != null) {

  2.    return bean;

  3. }

如果代理物件不為空,則直接傳回代理物件,這一步驟有非常重要的作用,Spring 後續實現 AOP 就是基於這個地方判斷的。

  1.    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {

  2.        Object bean = null;

  3.        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {

  4.            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

  5.                Class> targetType = determineTargetType(beanName, mbd);

  6.                if (targetType != null) {

  7.                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

  8.                    if (bean != null) {

  9.                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);

  10.                    }

  11.                }

  12.            }

  13.            mbd.beforeInstantiationResolved = (bean != null);

  14.        }

  15.        return bean;

  16.    }

這個方法核心就在於 applyBeanPostProcessorsBeforeInstantiation() 和 applyBeanPostProcessorsAfterInitialization() 兩個方法,before 為實體化前的後處理器應用,after 為實體化後的後處理器應用,由於本文的主題是建立 bean,關於 Bean 的增強處理後續 LZ 會單獨出博文來做詳細說明。

建立 bean

如果沒有代理物件,就只能走常規的路線進行 bean 的建立了,該過程有 doCreateBean() 實現,如下:

  1.   protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)

  2.            throws BeanCreationException {

  3.        // BeanWrapper是對Bean的包裝,其介面中所定義的功能很簡單包括設定獲取被包裝的物件,獲取被包裝bean的屬性描述器

  4.        BeanWrapper instanceWrapper = null;

  5.        // 單例模型,則從未完成的 FactoryBean 快取中刪除

  6.        if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName);

  7.        }

  8.        // 使用合適的實體化策略來建立新的實體:工廠方法、建構式自動註入、簡單初始化

  9.        if (instanceWrapper == null) {

  10.            instanceWrapper = createBeanInstance(beanName, mbd, args);

  11.        }

  12.        // 包裝的實體物件

  13.        final Object bean = instanceWrapper.getWrappedInstance();

  14.        // 包裝的實體物件的型別

  15.        Class> beanType = instanceWrapper.getWrappedClass();

  16.        if (beanType != NullBean.class) {

  17.            mbd.resolvedTargetType = beanType;

  18.        }

  19.        // 檢測是否有後置處理

  20.        // 如果有後置處理,則允許後置處理修改 BeanDefinition

  21.        synchronized (mbd.postProcessingLock) {

  22.            if (!mbd.postProcessed) {

  23.                try {

  24.                    // applyMergedBeanDefinitionPostProcessors

  25.                    // 後置處理修改 BeanDefinition

  26.                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

  27.                }

  28.                catch (Throwable ex) {

  29.                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,

  30.                            "Post-processing of merged bean definition failed", ex);

  31.                }

  32.                mbd.postProcessed = true;

  33.            }

  34.        }

  35.        // 解決單例樣式的迴圈依賴

  36.        // 單例樣式 & 執行迴圈依賴&當前單例 bean 是否正在被建立

  37.        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

  38.                isSingletonCurrentlyInCreation(beanName));

  39.        if (earlySingletonExposure) {

  40.            if (logger.isDebugEnabled()) {

  41.                logger.debug("Eagerly caching bean '" + beanName +

  42.                        "' to allow for resolving potential circular references");

  43.            }

  44.            // 提前將建立的 bean 實體加入到ectFactory 中

  45.            // 這裡是為了後期避免迴圈依賴

  46.            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

  47.        }

  48.        /*

  49.         * 開始初始化 bean 實體物件

  50.         */

  51.        Object exposedObject = bean;

  52.        try {

  53.            // 對 bean 進行填充,將各個屬性值註入,其中,可能存在依賴於其他 bean 的屬性

  54.            // 則會遞迴初始依賴 bean

  55.            populateBean(beanName, mbd, instanceWrapper);

  56.            // 呼叫初始化方法

  57.            exposedObject = initializeBean(beanName, exposedObject, mbd);

  58.        }

  59.        catch (Throwable ex) {

  60.            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {

  61.                throw (BeanCreationException) ex;

  62.            }

  63.            else {

  64.                throw new BeanCreationException(

  65.                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);

  66.            }

  67.        }

  68.        /**

  69.         * 迴圈依賴處理

  70.         */

  71.        if (earlySingletonExposure) {

  72.            // 獲取 earlySingletonReference

  73.            Object earlySingletonReference = getSingleton(beanName, false);

  74.            // 只有在存在迴圈依賴的情況下,earlySingletonReference 才不會為空

  75.            if (earlySingletonReference != null) {

  76.                // 如果 exposedObject 沒有在初始化方法中被改變,也就是沒有被增強

  77.                if (exposedObject == bean) {

  78.                    exposedObject = earlySingletonReference;

  79.                }

  80.                // 處理依賴

  81.                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {

  82.                    String[] dependentBeans = getDependentBeans(beanName);

  83.                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);

  84.                    for (String dependentBean : dependentBeans) {

  85.                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {

  86.                            actualDependentBeans.add(dependentBean);

  87.                        }

  88.                    }

  89.                    if (!actualDependentBeans.isEmpty()) {

  90.                        throw new BeanCurrentlyInCreationException(beanName,

  91.                                "Bean with name '" + beanName + "' has been injected into other beans [" +

  92.                                        StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +

  93.                                        "] in its raw version as part of a circular reference, but has eventually been " +

  94.                                        "wrapped. This means that said other beans do not use the final version of the " +

  95.                                        "bean. This is often the result of over-eager type matching - consider using " +

  96.                                        "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");

  97.                    }

  98.                }

  99.            }

  100.        }

  101.        try {

  102.            // 註冊 bean

  103.            registerDisposableBeanIfNecessary(beanName, bean, mbd);

  104.        }

  105.        catch (BeanDefinitionValidationException ex) {

  106.            throw new BeanCreationException(

  107.                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);

  108.        }

  109.        return exposedObject;

  110.    }

整體的思路:

  1. 如果是單例樣式,則清除 factoryBeanInstanceCache 快取,同時傳回 BeanWrapper 實體物件,當然如果存在。

  2. 如果快取中沒有 BeanWrapper 或者不是單例樣式,則呼叫 createBeanInstance() 實體化 bean,主要是將 BeanDefinition 轉換為 BeanWrapper

  • MergedBeanDefinitionPostProcessor 的應用

  • 單例樣式的迴圈依賴處理

  • 呼叫 populateBean() 進行屬性填充。將所有屬性填充至 bean 的實體中

  • 呼叫 initializeBean() 初始化 bean

  • 依賴檢查

  • 註冊 DisposableBean

doCreateBean() 完成 bean 的建立和初始化工作,內容太多,這裡就只列出整體思路,下文開始將該方法進行拆分進行詳細講解,分佈從以下幾個方面進行闡述:

  • createBeanInstance() 實體化 bean

  • populateBean() 屬性填充

  • 迴圈依賴的處理

  • initializeBean() 初始化 bean

【死磕 Spring】----- IOC 之分析各 scope 的 bean 建立

【死磕 Spring】----- IOC 之開啟 bean 的載入

【死磕 Spring】----- IOC 之從單例快取中獲取單例 bean

【死磕 Spring】----- IOC 之parentBeanFactory 與依賴處理

【死磕 Spring】----- IOC 之 IOC 初始化總結

END


>>>>>> 加群交流技術 <<<<<<

贊(0)

分享創造快樂

© 2022 知識星球   網站地圖