Spring解析之IoC:bean的加載(三)

前言
計劃趕不上變化,原本年前準備將這個主題告一段落,但各種工作之外的事接踵而至,且剩下的工作量依然超過我的預期,只能拖到現在,前段時間接了商業化廣告的活,源碼看得多了自己寫時總不自覺的按更高的標準要求自己,一遍遍的修改重構,這可能就是所謂的潛移默化吧
bean加載三部曲中的第一部按照doGetBean(String, Class<T>, Object[], boolean)自上而下的代碼結構將加載分為if-else兩段,前段在getBean顯式調用時返回單例對象以及自定義FactoryBean生成bean時調用,其中的重點為部分解決循環依賴的“三層緩存”和getObjectForBeanInstance(Object, String, String, RootBeanDefinition),后段由初始化過程中隱式調用以及獲得多例對象時調用,在第二部中介紹了隱式調用的整個流程,最后剩下createBean(String, RootBeanDefinition, Object[])核心創建bean尚未分析,本篇文章就與這部分內容相關

由于只剩下createBean這個核心方法了,那么我們還是像前面將核心方法分成幾塊講解

圖1. AbstractAutowireCapableBeanFactory的createBean(String, RootBeanDefinition, Object[])

標注1生成最終能實例化的Class類型,但并沒有根據Class實例化出對象
圖2. 解析出可實例化的Class

在分析解析XML并將解析后內容封裝成GenericBeanDefinition過程中曾今說過<bean>class屬性與GenericBeanDefinition父類AbstractBeanDefinitionObject beanClass對應,這里mbd.hasBeanClass()就是判斷beanClass是否instanceof Class,如果是直接返回,因為我們要的就是Class類型,不是的話最終調用doResolveBeanClass(RootBeanDefinition, Class<?>),該方法依然是個中轉方法,主要根據傳入的typesToMatch生成特定的ClassLoader,之后還要調用RootBeanDefinition#resolveBeanClass,根據特定的加載器或者默認加載器加載出class屬性對應的Class對象,其生成Class的動作又委派給工具類ClassUtils
圖3. 使用ClassUtils創建出Class

我們將創建Class流程分成三部分:第一部分主要處理一些“原始”數據類型,舉個簡單的例子我在XML中配置如下

<bean id="myInt" class="java.lang.Integer">

我定義的對象就是“原始”數據類型,這種類型的Class處理就由第一部分負責,具體是怎么做的呢,實際上是兩個Map,一個是resolvePrimitiveClassName(String)中隱含的primitiveTypeNameMap,另一個就是commonClassCache,對應的代碼給大家看一下

圖4. 原始Class元素的塞入

ClassUtils加載時就會將基本數據類型,基本數據類型對應的包裝類型,基本數據類型數組和包裝類型數組等Class放入兩個Map中,如果我們配置的類屬于其中的一個直接返回對應的Class即可。圖3紅框2內代碼處理的類型為java.lang.String[][Ljava.lang.String;[[Ljava.lang.String;,第一個我可以嘗試模擬出來,后兩個實在沒搞明白怎么模擬,有知道的讀者麻煩告知。如果之前根據typesToMatch生成了特定的類加載器就用這個ClassLoader加載Class,否則使用默認加載器,默認類加載是首先使用當前線程類加載器,如果還是沒有獲取到使用當前類的類加載器。這一系列的操作后RootBeanDefinition中的beanClass屬性除了異常情況就都是對應<bean>生成對象的Class
回到圖1標注2代碼是對<lookup-method><replaced-method>兩種method injection方式的預處理,在Spring解析之IoC:<bean>解析及Bean的注冊中我們沒有詳細闡述兩個標簽的解析存儲過程,在這里大致了解一下,兩個標簽解析后分別封裝在LookupOverrideReplaceOverride中,兩個對象都繼承自抽象父類MethodOverride,而多個該類對象又存放在AbstractBeanDefinition成員變量MethodOverrides methodOverrides中,下面我已<lookup-method>舉個例子,說明下此類標簽的用法已經解決的問題
<lookup-method>標簽最重要的一個用途就是在單例對象內注入一個多例對象,我們知道構造器注入和setter注入是兩種最常用的注入方式,但如果說到實現單例對象中注入多例對象需求,這兩種注入方式都鞭長莫及,因為在Spring創建出單例對象時相關的依賴注入流程都已經完成,也就是說依賴的對象只能注入一次,為了解決這個問題,Spring提出了method injection方法注入,我們先來例子讓大家有個直觀的認識。首先定義一個單例對象SingletonService
圖5. SingletonService

該類是一個抽象類,存在返回多例對象的抽象方法,再定義一個多例對象的抽象接口Prototype
圖6. Prototype接口

有接口了必然有就有一個或多個實現,這里為了簡便只提供了一個實現類
圖7. Prototype接口的實現類MyPrototype

在XML中進行<bean>配置
圖8. 相關XML配置

實現類scope = prototype否則就失去了動態注入的意義了,lookup-methodname獲得多例對象的抽象方法名稱,bean為返回bean的類型,比如這里就是任何實現了Prototype接口的實現類id/name,如果讀者想跟深入了解的話,我給出一篇文章Method Injection。說了一堆做鋪墊再來看圖1標注2做了什么
圖9. AbstractBeanDefinition的prepareMethodOverrides()

上面說過所有的方法注入標簽都存放在methodOverrides,遍歷所有的MethodOverride,根據<lookup-method><replaced-method>中抽象方法名(name屬性的值)和抽象類中所有同名的抽象方法比較,算出符合name的抽象方法數量,為什么要這樣呢?我試驗了一下,結果是可能存在同名重載方法,最簡單的例子就是同名抽象方法但參數不同,但<lookup-method><replaced-method>中有無法配置抽象方法的參數,所以需要全部計數。那為什么但只存在一個符合抽象方式時setOverloaded(false)呢?其實讀者可以將這里的判斷和下面的使用分為兩部分考慮,如果存在多個重載方法,那么使用時必定要再次判斷到底配置的是哪一個,如果只有一個符合要求這里加上一個標識,在下面使用的時候就省去了再次判斷的開銷
圖1的標注3主要的目的在注釋上寫的還是比較清楚的,給用戶一個機會可以返回目標對象的代理實例,如果返回不為null,即返回了代理對象就直接返回,在正式說這部分邏輯之前還得先補充一個知識,在前面說過Spring提供后處理器接口BeanPostProcessor,提供了在初始化對象后修改實例的能力,該接口有很多子接口,后面有機會會一一介紹,現在先說一種InstantiationAwareBeanPostProcessor,除了父類提供的兩個待實現接口外又提供了另外三種,分別為Object postProcessBeforeInstantiation(Class<?>, String)boolean postProcessAfterInstantiation(Object, String)PropertyValues postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String),為了更好的說明還是舉個例子
圖10. InstantiationAwareBeanPostProcessorImpl

InstantiationAwareBeanPostProcessorImpl實現了接口所有的方法,并做了一些打印以便觀看現象,在XML中也加上簡單的配置

<bean id="student" class="com.xiaomi.bean.Student">
    <property name="name" value="zhangsan"></property>
</bean>
<bean id="instantiationAwareBeanPostProcessor" class="com.xiaomi.bean.InstantiationAwareBeanPostProcessorImpl"></bean>

運行并兩次獲得Student實例打印,結果如下

圖11. InstantiationAwareBeanPostProcessor例子運行結果

很明顯在生成Student實例前調用了postProcessBeforeInstantiation,那么我們可以在這個時候對目標對象進行一定的包裝或者處理,達到類似AOP的目的,之后真正創建了Student實例,創建完成進行屬性的注入,在注入之前會調用postProcessPropertyValues,在該方法中我們將原本注入的zhangsan替換成了lisi,“調包”后Spring調用了setter進行屬性注入,最后才調用父接口BeanPostProcessor的兩個方法,這個順序之前已經說過,沒什么意外的。我們獲得了兩次Student對象由打印語句發現實現InstantiationAwareBeanPostProcessor只會在創建對象前調用一次,當然,如果這里Student是多例,自然就會被第二次處理。講到這里我們將postProcessBeforeInstantiation注釋去除,在運行一遍看看結果有什么不同
圖12. 去除postProcessBeforeInstantiation注釋部分去除運行結果

我們發現只剩下postProcessBeforeInstantiationpostProcessAfterInitialization兩個方法的調用(Student構造器也是在postProcessBeforeInstantiation中顯式調用的,而不是Spring容器創建對象時默認調用),為什么會出現這種情況,其實我們回到源碼看一下處理邏輯就都清楚了
圖13. AbstractAutowireCapableBeanFactory的resolveBeforeInstantiation(String, RootBeanDefinition)

上圖代碼對應圖1的標注3,在初始化時Spring會檢測是否存在自定義類實現了InstantiationAwareBeanPostProcessor接口,如果存在會將DefaultListableBeanFactory中成員變量hasInstantiationAwareBeanPostProcessors置為true,這里的boolean hasInstantiationAwareBeanPostProcessors()判斷通過調用第一個下劃線方法
圖14. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInstantiation(Class<?>, String)

邏輯很簡單,首先獲得所有后處理器,判斷其中所有實現InstantiationAwareBeanPostProcessor的類,進而調用它的postProcessBeforeInstantiation,最后將我們自己實現方法的返回值返回。resolveBeforeInstantiation根據是否返回對象(代理)判斷是否調用applyBeanPostProcessorsAfterInitialization(RootBeanDefinition, String)
圖15. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization(Object, String)

方法中調用了BeanPostProcessorpostProcessAfterInitialization(Object, String),再看看圖1的總流程,如果bean != null就直接返回,聯想上面的例子bean != null實際上就是我們生成代理的情況,那就是說如果有代理直接返回代理對象,且只會調用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiationBeanPostProcessor#postProcessAfterInitialization,所表現出來的結果正是例子圖12的樣子,既然調了包返回了代理那就沒有必要再創建原始的目標類了,否則進入圖1標注4創建目標類實例的代碼,代碼清單2

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
    //    (1)
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    //    (2)
    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) {
      //    (3)
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      mbd.postProcessed = true;
    }
  }

  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  //    (4)
  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");
    }
    //    (5)
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
      public Object getObject() throws BeansException {
        return getEarlyBeanReference(beanName, mbd, bean);
      }
    });
  }

  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
    //    (6)
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
      //    (7)
      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);
    }
  }
  //    (8)
  if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
      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.");
        }
      }
    }
  }

  // Register bean as disposable.
  try {
    //    (9)
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
  }
  catch (BeanDefinitionValidationException ex) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  }

  return exposedObject;
}

Spring首先創建了一個bean的包裝類,按照官方文檔上的說法提供該類的好處有:1.提供了設置單個和成塊屬性值的功能,可以獲得屬性的descriptors,查詢屬性是否可讀可寫;2.支持屬性的無限嵌套;2.具有不改變目標類代碼情況下,添加PropertyChangeListenersVetoableChangeListeners功能等。還想深入了解該類的讀者可以看Bean manipulation and the BeanWrapper。如果緩存中已經存在我們后續要操作的BeanWrapper,直接移除取出做后續處理如標注1,如果暫時不存在該對象標注2處代碼進行創建,此處是getBean核心邏輯之一,Spring根據創建對象方式的不同又在該方法內部分為三部分:1.根據factory-method工廠方法進行創建;2.匹配一個或多個參數的有參構造器創建;3.默認無參進行實例化。后續會用專門的文章對這三種方式的創建流程進行分析。
到目前為止我們已經遇過兩個后處理器相關的接口,一個是BeanPostProcessor,另一個是它的子接口InstantiationAwareBeanPostProcessor,標注3又會引出一個BeanPostProcessor的子接口MergedBeanDefinitionPostProcessor,除了父接口的兩個方法外還需實現該接口的類給出postProcessMergedBeanDefinition(RootBeanDefinition, Class<?>, String),前面我們說過如果<bean>存在父子關系,Spring在解析之后會將父子關系的<bean>映射成為RootBeanDefinition,那么該后處理器及待實現方法就是在做RootBeanDefinition的后處理工作
標注4根據:1.是否單例;2.是否允許循環引用標志;3.beanName對應的bean是否正在創建中三個條件決定是否允許“早期單例暴露”。同樣的,我們說過Spring中部分解決循環依賴的第三級緩存正是標注5的代碼,如果判斷允許“早期單例暴露”就將創建該單例對象的ObjectFactory存入三級緩存singletonFactories。當要獲得單例對象時Spring會調用自定義ObjectFactorygetObject(),其實就是這里的getEarlyBeanReference(String, RootBeanDefinition, Object)

圖16. 獲得早期單例對象引用getEarlyBeanReference(String, RootBeanDefinition, Object)

這里又出現一個新的后處理器SmartInstantiationAwareBeanPostProcessor,它繼承自InstantiationAwareBeanPostProcessor,新提供了三個待實現方法:1.Class<?> predictBeanType(Class, String)用于預測返回beanClass類型,一般在隱式初始化時調用;2.Constructor<?>[] determineCandidateConstructors(Class<?>, String)存在多個構造器時用于決定使用哪個進行類的初始化,在上面說過的通過有參構造器創建對象時調用;3.Object getEarlyBeanReference(Object, String)用于獲得早期暴露的單例對象引用,和MergedBeanDefinitionPostProcessor類似,一般不需要我們人為擴展,而是Spring內部自己使用。標注6主要是對BeanWrapper進行屬性的填充,代碼清單3

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
                //    (1)
        if (bw == null) {
            if (!pvs.isEmpty()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                return;
            }
        }

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        boolean continueWithPropertyPopulation = true;
                //      (2)
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }
                //      (3)
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
                //      (4)
        if (hasInstAwareBpps || needsDepCheck) {
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }
                //      (5)
        applyPropertyValues(beanName, mbd, bw, pvs);
    }

Spring初始化解析XML配置文件時,所有的<property>會放入PropertyValue的集合中,集合又被封裝入MutablePropertyValues對象中。標注1的判斷邏輯是,如果不存在是<bean>對應實體BeanWrapper但還存在解析出的屬性,肯定出現問題了,拋出異常;如果不存在實體也不存在屬性,那也沒有繼續往下解析的必要了,直接返回。標注2又一次用到了上面講過的InstantiationAwareBeanPostProcessor,如果存在實現該接口后處理器,則調用boolean postProcessAfterInstantiation(Object, String),進行對象實例創建后的處理,這里依然存在一個短路操作,如果方法返回false,continueWithPropertyPopulation為假,如果該值非真,也無需進行后處理器對于屬性處理等后續操作了。標注3很明顯涉及按名稱、按類型兩種屬性注入方式,后續會有專門文章分析注入原理,這里不做闡述。標注4根據是否有InstantiationAwareBeanPostProcessor或依賴檢查標識判斷是否進行屬性的“調包”處理,處理的過程就是InstantiationAwareBeanPostProcessor自有方法的最后一個PropertyValues postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String)。到這里Spring得到了所有需要填充的屬性(兩種方式注入的、后處理器調包的),最后調用標注5處代碼真正將屬性設置進BeanWrapper對象中,代碼清單4

    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        if (pvs == null || pvs.isEmpty()) {
            return;
        }

        MutablePropertyValues mpvs = null;
        List<PropertyValue> original;

        if (System.getSecurityManager() != null) {
            if (bw instanceof BeanWrapperImpl) {
                ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
            }
        }
                //    (1)
        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            if (mpvs.isConverted()) {
                // Shortcut: use the pre-converted values as-is.
                try {
                    bw.setPropertyValues(mpvs);
                    return;
                }
                catch (BeansException ex) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                }
            }
            original = mpvs.getPropertyValueList();
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }
                //      (2)
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
                //      (3)
        // Create a deep copy, resolving any references for values.
        List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
        boolean resolveNecessary = false;
        for (PropertyValue pv : original) {
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                                //    (4)
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                                        //    (5)
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                //     (6)
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(pv);
                }
                //    (7)
                else if (convertible && originalValue instanceof TypedStringValue &&
                        !((TypedStringValue) originalValue).isDynamic() &&
                        !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                    pv.setConvertedValue(convertedValue);
                    deepCopy.add(pv);
                }
                else {
                    resolveNecessary = true;
                    deepCopy.add(new PropertyValue(pv, convertedValue));
                }
            }
        }
        //    (8)
        if (mpvs != null && !resolveNecessary) {
            mpvs.setConverted();
        }

        // Set our (possibly massaged) deep copy.
        try {
            //    (9)
            bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
        }
    }

標注1首先判斷所有屬性整體是否都被轉換,如果是就不需要下面的步驟,直接賦值返回;如果并不是所有屬性已經轉換過,就會首先按標注2獲取用戶自定義的類型轉換器。標注3創建了新集合用于存放轉換后的結果,之后遍歷每一個屬性并依次判斷轉換與否,沒有轉換則標注4根據上面創建的解析器valueResolver對該值進行解析,我們知道除了最普通的String外Spring支持多種形式的屬性配置,該方法中就包含了幾乎所有類型屬性的解析流程,這里給出一張表格涉及所有解析的類型已經該類型在讀取XML時對應的封裝類型

真實類型 封裝類型
Array ManagedArray
List ManagedList
Set ManagedSet
Map ManagedMap
Bean BeanDefinitionHolder
ref引用Bean RuntimeBeanReference
idref RuntimeBeanNameReference
Properties ManagedProperties
String TypedStringValue
SPEL表達式 TypedStringValue

如果判斷該屬性是可轉換的convertible = true,就會開始標注5的自定義轉換器的轉換,如果原始值originalValue與經過Spring內置解析器解析后的值resolvedValue相同,且可轉換標識convertible為真說明必定經過了自定義轉換的步驟(不管是否真的轉換過),此時用最新的convertedValue替換老值,如果標識6所示。實際上我覺得標注6和7具有相同的目的,只要convertible為真就說明最新的值必然為convertedValue,那也必然要更新屬性封裝對象對應字段了。標注8將屬性對象設為整體已轉化,與方法一開始根據此判斷直接返回還是開始轉換相呼應。標注9終于將解析后數組封裝入MutablePropertyValues,再塞入BeanWrapper中,至此創建對象中的屬性填充流程分析完畢
接著代碼清單2分析看標注7,“初始化”Bean

圖17. AbstractAutowireCapableBeanFactory的initializeBean(String, Object, RootBeanDefinition)

該方法嚴格意義上來說并不是做了Bean的初始化,因為此時參數的bean已經是實例化完成的對象了,只不過該對象還在Spring內部,并沒有暴露給用戶,方法內主要做了三件事:1.感知類對象相關屬性的設置;2.后處理器方法的處理;3.init-method屬性的處理。我們一個個來看,如果bean實現了BeanNameAwareBeanClassLoaderAwareBeanFactoryAware三種Aware接口中的一個或多個,則調用對應的實現方法,雖然大家可能對這三者不太熟悉,另一個Aware接口ApplicatonContextAware相信大家都用過,該接口的setApplicationContext(ApplicationContext)能讓我們得到Spring的上下文對象,ApplicatonContextAware和這里的三個接口處理方式如出一轍,只是觸發時機不同,前者是在bean后處理階段觸發,后者則是在這里的Spring容器生命周期初始階段
紅線2又碰到bean后處理器了,后處理器有多種變體,上面提到過InstantiationAwareBeanPostProcessorSmartInstantiationAwareBeanPostProcessor,并且在Spring解析之IoC:bean的加載(一)中也用例子的形式詳解了BeanPostProcessor的用法,有了前面的基礎這里后處理器也就非常好理解了,標注2和4分別對應了BeanPostProcessorObject postProcessBeforeInitialization(Object, String)Object postProcessAfterInitialization(Object, String),方法內會遍歷所有實現BeanPostProcessor接口的類調用每個類的上述兩個方法。同樣的在Spring解析之IoC:bean的加載(一)BeanPostProcessor例子中我們曾經說過后處理器的before/after是相對于實現了InitializingBean接口afterPropertiesSet()方法的執行時刻來說的,那么看圖中標注3不是正處在前后方法中間嗎?我們有理由猜測invokeInitMethods(String, Object, RootBeanDefinition)就是對于afterPropertiesSet()的處理
圖18. AbstractAutowireCapableBeanFactory的invokeInitMethods(String, Object, RootBeanDefinition)

橫線代碼驗證了我們的猜測,但是我們發現除了調用afterPropertiesSet()外還有一處紅框內的邏輯沒有想到。之前說過Spring提供了三對在bean初始化和銷毀時進行自定義操作的手段:1.@PostConstruct/@PreDestory;2.<bean>init-method/destroy-method屬性;3.實現InitializingBean/DisposableBean接口,這段代碼就是針對第二種情況進行的處理,方法invokeCustomInitMethod(String, Object, RootBeanDefinition)內部做了一些權限控制后得到配置init-methodMethod對象,最終通過反射調用該方法
初始化bean方法分析完畢,我們回到圖17從宏觀看看各個流程的步驟,其實這就是Spring容器中bean生命周期那張圖的執行流程
圖19. Spring中Bean的生命周期

  1. Instantiate對應代碼清單2標注2instanceWrapper = createBean(beanName, mbd, args);第一次創建bean
  2. Popluate properties對應代碼清單3整個屬性填充過程
  3. BeanNameAware's setBeanName()對應圖17invokeAwareMethods(String, Object)處理第一個BeanNameAware流程
  4. BeanFactoryAware's setBeanFactory()對應圖17invokeAwareMethods(String, Object)處理第三個BeanFactoryAware流程
  5. Pre-initializatioin beanPostProcessors對應圖17標注2代碼applyBeanPostProcessorsBeforeInitialization(Object, String)
  6. InitializingBean's afterPropertiesSet()對應圖18紅線處邏輯
  7. Call custom init-method對應圖18紅框內代碼
  8. Post-initialization beanPostProcessors對應圖17標注4代碼applyBeanPostProcessorsAfterInitialization(Object, String)

生命周期中的最后兩個狀態雖然現在沒有遇到,但大家看名字就很明顯9與6InitializingBean接口對應,一個初始化一個銷毀;10destroy-method屬性和7init-method對應。記得剛學習Spring時,這個生命周期是永遠記不住的知識點,但整個源碼分析下來就清清爽爽了
回到主線代碼清單2,標注8處代碼看上去一大堆,實際這段被執行到的可能性很小,earlySingleton = true會調用我們曾今說的Object getSingleton(String, boolean),我們將方法放在現在的“語境”中再分析一下

圖20. 特定場景下的Object getSingleton(String, boolean)執行流程圖

讀者需要知道的是,這段代碼在new ClassPathXmlApplicationContext()隱式調用和getBean多例對象時才有可能調用,對于多例來說earlySingletonExposure = false,意味著代碼不會執行;對于隱式調用allowEarlyReference = false說明紅框處內的代碼不會執行,再結合代碼清單2標注5addSingletonFactory(String, ObjectFactory)可以推得只存在singletonObjects中已有beanName對應對象這一種可能,整個邏輯代碼才會往下執行,介于此該流程就不繼續往下分析了。最后看下代碼清單2標注9
圖21. AbstractBeanFactory的registerDisposableBeanIfNecessary(String, Object, RootBeanDefinition)

該方法的主要作用是對實現Disposable接口的對象進行注冊,注意,這里只是注冊,因為銷毀必定是在容器關閉時調用的,而這里所有的一切都是對象初始化流程,所以只能是注冊,并且只有單例對象且實現Disposable接口的destroy()或者實現DestructionAwareBeanPostProcessor接口的postProcessBeforeDestruction(Object, String)才會真正注冊。注冊分為兩大塊,單例的和自定義scope的。單例對象銷毀注冊調用registerDisposableBean(String, DisposableBean),將beanName-bean映射放入Map<String, Object> disposableBeans即結束;自定義scope由于需要實現Scope接口,其中就包含這里的registerDestructionCallback(String, Runnable)。至此Spring獲取bean邏輯基本講解完畢,階段性勝利,哈哈

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容