1. 程式人生 > >【轉】Spring核心之BeanFactory 一張圖看懂getBean全過程

【轉】Spring核心之BeanFactory 一張圖看懂getBean全過程

Spring通過資源載入器載入相應的XML檔案,使用讀取器讀取資源載入器中的檔案到讀取器中,在讀取過程中,解析相應的xml檔案元素,轉化為spring定義的資料結BeanDefinition,把相應的BeanDefinition註冊到登錄檔中。登錄檔中包含的BeanDefinition的資料結構,沒有經過加工處理過,無法得到我們想要的bean物件。
我們如何得到Bean物件,spring都做了那些工作?BeanFactory提供了多種方式得到bean物件,getBean()方法是最核心得到bean物件
getBean主要由AbstractBeanFactory、AbstractAutowireCapableBeanFactory、以及DefaultListableBeanFactory實現
AbstractBeanFactory 實現了依賴關係處理
AbstractAutowireCapableBeanFactory 實現了bean的create過程
DefaultListableBeanFactory 實現了BeanDefinition的管理
自己看原始碼畫出了這張流程圖,根據這張圖來介紹spring是怎麼一步步的得到bean物件的。




 

以下是getBean方法的實現流程。

getBean經過方法過載後,最終呼叫的是doGetBean方法,

需要的方法引數如下:
1.name 你要得到bean物件的名稱 不能為空
2.requiredType 這個bean物件的Class型別,可以為null
3.args 可以為null,如果有引數,則代表在找到這個bean定義後,通過構造方法或工廠方法或其他方法傳入args引數來改變這個bean例項。
spring 工廠開始自動化處理了:
1.name引數的處理: --xml中的alias屬性
    如果有&開頭的name代表了FactoryBean的例項名稱,則要去掉這個字首
    從別名登錄檔中解析出規範的名稱。這個別名登錄檔是一個final map,在載入xml時讀入bean的別名到該final map中。
2.BeanDefinition的查詢: --xml中的name屬性
    得到規範的名字,然後拿去檢索BeanDefinitionRegistry登錄檔中是否存在該BeanDefinition,這個BeanDefinitionRegistry登錄檔,也是一個 final map,裡面已經存在了使用者定義的BeanDefinition,也是在載入xml時讀入BeanDefinition到登錄檔中的。
  如果不存在該BeanDefinition,並且沒有相應的父工廠則丟擲異常。
  有相應的父工廠,該工廠就把引數交給了父工廠去處理,父工廠就進入自己的doGetBean方法,重複步驟1工作,就形成遞迴迴圈處理。
  如果存在BeanDefinition.查詢該BeanDefinition開始進行合併屬性到RootBeanDefinition中。
3.合併BeanDefinition為RootBeanDefinition --xml中的parent屬性
  #1 如果BeanDefinition沒有定義父bean名稱,則建立一個新的RootBeanDefinition,並把BeanDefinition賦給新的RootBeanDefinition。
  #2如果這個BeanDefinition是一個RootBeanDefinition,則使用RootBeanDefinition clone出一個。
   如果BeanDefinition存在定義的父bean名稱,則通過這個父bean名稱查詢到父BeanDefinition重複步驟3遞迴迴圈處理。
   如果父bean名稱和當前的BeanDefinition名稱相同則使用父工廠的步驟3,遞迴迴圈處理。
   最後,建立一個新的RootBeanDefinition,並把父的BeanDefinition賦給新的RootBeanDefinition,
   並把當前BeanDefinition合併到這個新的RootBeanDefinition
 內部RootBeanDefinition在合併到外部的RootBeanDefinition的時候,但內部bean如果不是單例的,而外部的RootBeanDefinition是單例的,
這時候就需要把內部RootBeanDefinition的scope屬性賦值給外部RootBeanDefinition的scope屬性上。
4.是否是抽象的  --xml中的abstract屬性
  如果是抽象的直接拋異常,不能為例項化bean物件
  否則繼續
5.初始化bean的依賴關係 --xml中的depends-on屬性
 查詢該bean對應的所有依賴關係bean名稱,迴圈依賴bean名稱,註冊依賴關係,再次getBean遞迴查詢依賴bean。重複步驟1工作。
 它使用final ConcurrentHashMap執行緒安全的hash表來維護依賴關係
  1.dependentBeanMap key是bean name value是Set<String>包含了該beanname對應的bean依賴的bean名稱集合
  2.dependenciesForBeanMap key是bean name value是Set<String>包含了依賴該bean的bean名稱集合
6.Scope屬性決定建立bean時在不同的條件下進行 --xml中的scope屬性
當scope=singleton時,必須保證執行緒安全的建立單例bean。
當scope=prototype時,必須保證建立的bean是新的例項。
當scope為其他時這個暫時沒有研究怎麼,好像request session 使用Scope介面的方式進行。
建立的過程時基本相同的。

注:
從上面看,如果解析依賴很多的話,等待其他依賴bean建立完,是非常消耗效能的一件事,比如:單例建立需要加鎖。
所以spring使用DefaultSingletonBeanRegistry來本地快取來儲存建立過的物件。
1.final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256)
快取單例物件表,key beanname value是建立的單例物件,所有的單例物件都使用它來維護,它一般帶同步光環的,執行緒安全是首要任務。
2.final Map<String, ObjectFactory<?>> singletonFactories 簡單的快取singleton ObjectFactory,是為了提前曝光單例物件
3.final Map<String, Object> earlySingletonObjects key beanname vlaue快取提早曝光的物件,
該物件在建立期間只做了簡單建立過的物件,未初始化屬性及後續處理。即為單例,依賴關係建立指向引用就好了。
(滿足條件 1. 單例 2.當前的物件是在建立期間 3.允許曝光物件) 
4.final Set<String> registeredSingletons 註冊過的單例名稱集合
5.final Set<String> singletonsCurrentlyInCreation當前正建立的單例集合名稱
6.final Map<String, Set<String>> containedBeanMap key外部beanname value 內部beanname集合
7.dependentBeanMap key是bean name value是Set<String>包含了我依賴的bean名稱集合
8.dependenciesForBeanMap key是bean name value是Set<String>包含了依賴我的bean名稱集合
以上的快取對效能做了優化,有興趣可以去了解下。並且線上程安全方面是一個很好的例子。
比如:首先就是先去查快取singletonObjects存在,存在則直接使用,否則查earlySingletonObjects,
滿足條件了直接從快取中取,當然spring在建立複雜的bean時可以通過提前曝光簡單的物件,供依賴或後置處理程式使用,來加快訪問速度。

上面對查詢BeanDefinition、合併RootBeanDefinition及依賴關係建立後,就開始準備建立bean物件了。
7.確定型別  --xml class=""
  建立bean過程獲取該bean的型別是必須的。
  如何確定RootBeanDefinition的Class型別
      bean定義 
      1.如果存在BeanClass型別,則直接返回該型別     
      2.不存在就使用className,
    如果配置了BeanExpressionResolver 解析器,使用evaluate方法進行解析型別,(這個介面可以自定義來擴充套件
       解析定義的表示式,如SpringEL表示式)
    解析的物件是Class型別則返回該型別。
    如果返回的String 則使用工廠類載入器載入該className返回class型別
    這裡會嘗試多個型別載入器進行載入直到載入成功,比如當前工廠的類載入器、RootBeanDefinition的類載入,預設的類載入器等。
    在解析完型別後spring使用了快取來儲存className對應的型別,以便在以後中使用。
8.例項化之前的工作
  拷貝一個新的RootBeanDefinition供初始化使用。
  設定定義的覆蓋方法標記為已經載入過,避免參數型別檢查的開銷。這裡的覆蓋方法 --xml:lookup-method或replace-method
  下面該給客戶機會參與進來的機會了
  
   在例項化物件之前,spring設定了例項化後置處理器InstantiationAwareBeanPostProcessor,供外部控制或處理一些業務。
   比如代理這個物件,不需要spring例項化建立bean等,這是一個擴充套件點。使用者可以自己加入自己的例項化後置處理器,來處理一些業務。
   首先滿足合成的RootBeanDefinition不是應用程式本身定義的,並且工廠中已經包含一個或多個InstantiationAwareBeanPostProcessor處理器,
   滿足條件後,為了供InstantiationAwareBeanPostProcessor的方法postProcessBeforeInstantiation(Class<?> beanClass, String beanName)呼叫,
   需要確定 beanClass的目標型別,即要返回的bean物件的型別,如果返回null將不會呼叫當前迴圈中後續的BeanPostProcessor
   1.如果定義bean建立使用普通方式來建立就和上面的步驟7一樣,找到對應的beanClass返回
   2.使用(靜態)工廠方法定義則使用下面的流程來確定型別: factory-bean="carFactory" factory-method="getCar"
    1.如果RootBeanDefinition.resolvedFactoryMethodReturnType為class型別,則返回
        2.如果不是,得到 factory-bean對應的名稱carFactory,
    如果沒有定義factory-bean則說明使用了當前類做工廠,則呼叫7的步驟得到工廠型別
    確定carFactory型別
            1.如果carFactory存在則先從單例池中查詢有沒對應工廠bean物件,如果有返回該工廠bean的class型別,
         如果型別是FactoryBean型別則呼叫FactoryBean.getObjectType方法返回工廠bean型別,
        如果查詢的carFactory存在但值為null 則返回null
        2.如果單例中沒有這個物件,則使用父工廠中找,
        沒有父工廠,用carFactory這個名字查詢RootBeanDefinition
               然後得到這個RootBeanDefinition的BeanName進行7的步驟,得到工廠bean的返回型別,
               如果型別是FactoryBean型別則呼叫FactoryBean.getObjectType方法得到工廠bean的返回型別,    
 
        通過上面得到工廠bean的型別,但無法知道工廠方法返回的型別,通過反射,查詢到工廠bean的所有方法,
    如果有多個方法過載,則需要匹配設定的引數與方法引數那個最匹配,然後得到對應方法的返回型別。
       看完這個都暈了  太饒了可以跳過這段。
  確定完成RootBeanDefinition的beanClass以後,迴圈工廠中所有的BeanPostProcessor後置處理器集合,如果存在InstantiationAwareBeanPostProcessor
  例項,就呼叫該處理器的postProcessBeforeInstantiation方法返回一個Object物件,這個物件可以為空,也可以是使用者代理的一個物件。
   如果返回的物件不為null,說明你已經初始化過了物件,確定你要迴圈呼叫工廠中配置的BeanPostProcessor處理器集合中每一個
   postProcessAfterInitialization(Object bean, String beanName)方法,它是在物件初始化以後呼叫的。
  引數bean代表初始化後的bean,就是上面postProcessBeforeInstantiation返回的物件,返回值為Object。
  不管這兩個處理器怎麼處理,返回的最終物件為Object,如果不為空,將不會處理後續迴圈中的BeanPostProcessor,跳過bean建立過程。
  否則開始bean的例項化
8.開始例項化 ,相當於new一個物件
   首先物件是否是單例的,是的話就從單例快取中取是否存在該包裝過的例項BeanWrapper,如果存在就使用,否則就例項化。
   驗證beanClass是否可以例項化
   1.beanClass不能為null
   2.beanClass是public的或者是允許訪問的
9.自動注入例項化帶參的建構函式. 過程比較複雜我從網上看到分析的非常好

################################################################################
(1)建構函式引數的確定。

根據explicitArgs引數判斷。

如果傳入的引數explicitArgs不為空,那邊可以直接確定引數,因為explicitArgs引數是在呼叫Bean的時候使用者指定的,在BeanFactory類中存在這樣的方法:
 

    Object getBean(String name, Object... args) throws BeansException; 

在獲取bean的時候,使用者不但可以指定bean的名稱還可以指定bean所對應類的建構函式或者工廠方法的方法引數,主要用於靜態工廠方法的呼叫,而這裡是需要給定完全匹配的引數的,所以,便可以判斷,如果傳入引數explicitArgs不為空,則可以確定建構函式引數就是它。

快取中獲取。

除此之外,確定引數的辦法如果之前已經分析過,也就是說建構函式引數已經記錄在快取中,那麼便可以直接拿來使用。而且,這裡要提到的是,在快取中快取的可能是引數的最終型別也可能是引數的初始型別,例如:建構函式引數要求的是int型別,但是原始的引數值可能是String型別的"1",那麼即使在快取中得到了引數,也需要經過型別轉換器的過濾以確保引數型別與對應的建構函式引數型別完全對應。

配置檔案獲取。

如果不能根據傳入的引數explicitArgs確定建構函式的引數也無法在快取中得到相關資訊,那麼只能開始新一輪的分析了。

分析從獲取配置檔案中配置的建構函式資訊開始,經過之前的分析,我們知道,Spring中配置檔案中的資訊經過轉換都會通過BeanDefinition例項承載,也就是引數mbd中包含,那麼可以通過呼叫mbd.getConstructorArgumentValues()來獲取配置的建構函式資訊。有了配置中的資訊便可以獲取對應的引數值資訊了,獲取引數值的資訊包括直接指定值,如:直接指定建構函式中某個值為原始型別String型別,或者是一個對其他bean的引用,而這一處理委託給resolveConstructorArguments方法,並返回能解析到的引數的個數。

(2)建構函式的確定。

經過了第一步後已經確定了建構函式的引數,接下來的任務就是根據建構函式引數在所有建構函式中鎖定對應的建構函式,而匹配的方法就是根據引數個數匹配,所以在匹配之前需要先對建構函式按照public建構函式優先引數數量降序、非public建構函式引數數量降序。這樣可以在遍歷的情況下迅速判斷排在後面的建構函式引數個數是否符合條件。

由於在配置檔案中並不是唯一限制使用引數位置索引的方式去建立,同樣還支援指定引數名稱進行設定引數值的情況,如<constructor-arg name="aa">,那麼這種情況就需要首先確定建構函式中的引數名稱。

獲取引數名稱可以有兩種方式,一種是通過註解的方式直接獲取,另一種就是使用Spring中提供的工具類ParameterNameDiscoverer來獲取。建構函式、引數名稱、引數型別、引數值都確定後就可以鎖定建構函式以及轉換對應的引數型別了。

(3)根據確定的建構函式轉換對應的引數型別。

主要是使用Spring中提供的型別轉換器或者使用者提供的自定義型別轉換器進行轉換。

(4)建構函式不確定性的驗證。

當然,有時候即使建構函式、引數名稱、引數型別、引數值都確定後也不一定會直接鎖定建構函式,不同建構函式的引數為父子關係,所以Spring在最後又做了一次驗證。

(5)根據例項化策略以及得到的建構函式及建構函式引數例項化Bean。
###########################################################################################
10.例項化策略
首先判斷如果beanDefinition.getMethodOverrides()為空也就是使用者沒有使用replace或者lookup的配置方法,那麼直接使用反射的方式,簡單快捷,但是如果使用了這兩個特性,在直接使用反射的方式建立例項就不妥了,因為需要將這兩個配置提供的功能切入進去,所以就必須要使用動態代理的方式將包含兩個特性所對應的邏輯的攔截增強器設定進去,這樣才可以保證在呼叫方法的時候會被相應的攔截器增強,返回值為包含攔截器的代理例項。
 
11.工廠方法例項化bean 自己理解寫的比較簡陋看看就好了。
   建立一個BeanWapper物件,設定型別轉換器,屬性編輯器。
  如果定義的bean是使用工廠方法例項化:在xml中表示
    <!-- 
        靜態工廠方法建立例項 
        factory-method 靜態的工廠方法名字 
        constructor-arg靜態的工廠方法引數
     -->    
    <bean id="carFactory" class="com.zghw.spring.demo.demo.CarFactory" factory-method="getInstance" scope ="prototype">
        <constructor-arg name="factoryName" type="java.lang.String" value="static factory method"></constructor-arg>
    </bean>
    <!-- 動態工廠方式建立物件
        factory-bean 工廠物件
        factory-method 工廠方法名字 
        constructor-arg 工廠方法引數
     -->
     <bean id="carSub" class="com.zghw.spring.demo.demo.CarSub" scope ="prototype" factory-bean="carFactory" factory-method="getCar">
          <constructor-arg name="namefactory" type="java.lang.String"  value="AAAAAA"></constructor-arg>
          <constructor-arg name="countfactory" type="int" value="11122"></constructor-arg>
     </bean>
  1.得到工廠bean,
    如果定了factory-bean名稱 使用名稱通過getBean方法factory bean物件,代表這個方法是通過工廠方法建立非static方法
    如果沒有定義則則為null,說明靜態工廠方法 static方法
  2.工廠方法引數
     使用者傳入的引數:如果給定的引數不為空則使用該引數作為工廠方法引數
     則使用構造方法引數物件 快取中取:如果解析過的工廠方法引數物件存在,並且如果解析過的構造器引數物件也存在,就是用構造器引數物件
     快取中取不到,從BeanDefinition的原型引數物件,把eanDefinition的原型引數物件轉換為新的引數物件。 
    
  3.工廠方法
   通過beanClass型別,反射出方法集合,挑選出符合條件的方法(靜態工廠就匹配靜態方法否則匹配非靜態方法 然後匹配與定義的工廠方法名一樣的方法)
  然後使用反射機制 factoryMethod.invoken(factoryBean,args)得到返回的物件包裝成BeanWapper物件返回。
  這裡簡單的說了下,spring做的處理較複雜,有些點你沒有碰到過你不知道為什麼那麼多彎彎道道。
######################################################得到BeanWapper############################################################################

12.在例項化物件後包裝成了BeanWapper,接下來spring提供了一個擴充套件點,MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName),實現MergedBeanDefinitionPostProcessor介面的處理器,可以使用合併過的RootBeanDefinition做一些需要的業務,具體怎麼做spring是不操心的,在這個階段spring把工廠中所有的BeanPostProcessor後置處理器迴圈,如果是MergedBeanDefinitionPostProcessor的例項就呼叫該例項的方法postProcessMergedBeanDefinition幫你執行。

13.提前曝光bean物件   ref
   為什麼要提前曝光物件?
   在上面介紹地依賴關係建立的時候,物件如果依賴與另一個物件,則先建立依賴的物件。
如果物件A依賴與<單例>物件B,則先建立B物件,A就停留在上面步驟建立依賴關係的階段,等待B物件建立完成,
在B物件建立過程中,如果出現如下情況:
  1.B物件中有依賴A物件的屬性,如果A是單例的,B物件等待A建立完成,兩個互相等就形成了死迴圈。
  2.如果B物件,在初始化過程非常複雜,會導致效能問題。
可能還有其他情況。
總結出:提前曝光bean物件滿足條件:
    1.當前的bean是單例的,(原型物件是不會出現這問題的)
    2.並且支援迴圈引用 (spring提供設定是否支援迴圈引用,預設是允許的)
    3.當前的bean正在建立
為了解決上面的問題,我們在這個階段提前暴露物件,那麼對剛剛例項化的物件(如果你允許,spring提供設定是否支援迴圈引用)就可以暴露物件的引用供其他物件使用,不用需要等到該物件的初始完成後才使用。
   物件暴露在那,怎麼使用,單例登錄檔中有。對於物件依賴關係,暴露後的物件集合、及當前有哪些bean在建立、當前所有單例bean物件,都儲存在不同的final Map中,在上面我們已經簡單說過。有興趣可以網上查詢下該方面的資訊。
暴露這個bean物件固然好,但我想要對這個bean處理下在暴露行不,spring最懂你,它提供了SmartInstantiationAwareBeanPostProcessor介面,你可以實現該介面的方法:Object getEarlyBeanReference(Object bean, String beanName)
引數Object bean:BeanWapper中例項物件的引用,不關心你怎麼處理,不過方法返回的物件會在後續一直使用。

回到流程來,當滿足了提前曝光例項的條件後,spring迴圈工廠中所有的BeanPostProcessor,如果存在SmartInstantiationAwareBeanPostProcessor的例項,則呼叫getEarlyBeanReference方法返回物件,並把這個物件放入單例表中供提前使用,如果返回null將不會呼叫後續的BeanPostProcessor

14.注入屬性值之前
這個時候有使用者說物件已經例項化,不想注入屬性我想跳過或者我想在自動注入屬性之前做點什麼然後在注入屬性?
spring說好的沒問題,主動權給你,你需要實現InstantiationAwareBeanPostProcessor介面並實現方法boolean postProcessAfterInstantiation(Object bean, String beanName),你不想注入屬性就返回false,我們不會給你注入屬性的。想要注入屬性就返回true。引數bean如果值被改變了,以後我們就按照改變過的bean進行填充哦!
因為它是一個引用,注意哦!
在此階段spring工廠中存在後置處理器,會迴圈所有的BeanPostProcessor例項,如果是InstantiationAwareBeanPostProcessor的一個例項則呼叫postProcessAfterInstantiation方法,返回false就直接跳過填充屬性。
否則繼續填充bean屬性吧!
15.依賴注入:自動注入bean屬性  --xml autowire
首先檢查是否需要設定了自動注入
設定了自動注入,根據設定的依賴注入的方式不同,分為:
 1.根據屬性的名稱注入
 2.根據屬性的型別注入
  ==根據屬性名稱注入屬性bean:
    1.迴圈所有bean中的所有屬性描述符PropertyDescriptor
      取的滿足如下要求的屬性名:
    1.getWriteMethod即setter方法必須存在
        2.不是CGlib生成的屬性型別,也不存在ignoredDependencyTypes(可配置的)集合中的屬性型別,並且不是忽略介面(spring說是不能使用springbean工廠中介面作為屬性,比如BeanFactoryAware ApplicationContextAware)實現的屬性中包含有的方法
        3.定義的RootBeanDefition中不包含該屬性名字,因為是自動注入不會把定義的修改掉,所以才不包含的。
    4.這個屬性不能是“簡單的基本型別”,“簡單的基本型別”,包括了基本型別、包裝型別,String or other CharSequence, a Number, a Date,a URI, a URL, a Locale, a Class, or a corresponding array,BeanWapper擁有強大的屬性管理功能,它有PropertyEditor和TypeCovernt,很好處理這些"簡單基本型別",當然你可以擴充套件,自己的基本型別編輯器或轉化器。
篩選完不滿意的屬性,得到滿意的屬性名以後,
    確定屬性名稱是否存在bean
    1.(如果單例表中包含該bean || 有這個BeanDefinition定義) &&(屬性名稱不是&開頭的FactoryBean名稱 || 屬性是FactoryBean例項)
    2.如果存在父工廠,就去父工廠中去找
 1和2有一個返回true只代表存在該bean,但並不代表你得到的這個bean就是想要的。
 存在就使用依賴屬性名稱直接getBean就迴圈查詢吧。查詢完成注入屬性物件PropertyValue中,註冊類和屬性的依賴關係。
  ==根據屬性型別注入 這部分程式碼我是看暈了,道道太多,和註解的注入方式有關,這個簡單說下
   屬性型別注入和屬性名稱注入區別在於,屬性型別注入得到屬性名稱以後不是直接getBean物件返回,而以下篩選方式來匹配的。
    //1.如果這個型別是ObjectFactory的即我們之前在曝光的物件例項,則返回一個實現了ObjectFactory的描述府例項懶載入。
        2.查詢當前工廠中所有beanDefinition,排除一些不會存在的比如抽象的、沒有初始化過的等,然後匹配是否曝光的物件,
    FactoryBean物件,單例池中繼續匹配,最後才匹配名字相同的和型別相同的,然後當前工廠有父類工廠的話就去父類工廠匹配,
    合併當前工廠和父工廠匹配到的名字集合。匹配多個相同的bean名字就看那個bean是否定義了primary,只有一個的時候,然後getBean。否則就拋異常。
    這裡還牽涉到註解自動裝配的條件,這裡就不介紹了。
16.自動注入完屬性值後會合併RootBeanDefintion中定義的屬性值為一個新的屬性值集合,這個新的屬性值集合供以後解析和轉化到BeanWapper中。

到了該階段,spring為你提供了可以修改屬性引數的機會,實現InstantiationAwareBeanPostProcessor介面中的PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)方法,spring會迴圈工廠中配置過的每一個BeanPostProcessor例項,如果該例項是InstantiationAwareBeanPostProcessor,則會呼叫該介面的postProcessPropertyValues方法。
引數 pvs是上面說的新的屬性集合,pds代表了bean對應類的屬性描述符,不包含CGLib屬性或設定忽略的介面等,bean是BeanWapper包含的例項。你可以修改或移除
pvs,或者設定一些必須的引數,或者操作描述符pds,具體如何使用spring不關心。但最會使用你設定的返回的PropertyValues集合,來填充BeanWapper,如果你返回的PropertyValues為null,則跳過填充屬性,到初始化Bean。
如果設定了依賴檢查,對屬性進行依賴檢查,依賴檢查分三種:
    如果屬性描述符pds中的屬性有寫方法說明這個屬性可以使用,但PropertyValues pvs不包含此屬性時檢查:
    1.檢測全部,不符合,則丟擲異常。
        2.檢測簡單的屬性 不包含的這個屬性不是”簡單的型別“則丟擲異常。不符合
    3.檢查物件引用   包含的這個屬性是”簡單的型別“則丟擲異常。不符合
依賴檢測有問題則丟擲異常。

17.解析轉化bean屬性填充到BeanWapper    --xml <property>

依賴檢查成功後,就開始轉換返回的屬性集合PropertyValues pvs,它是RootBeanDefinition中的屬性集合,可能有的屬性並未做過解析轉換過,如果這個集合之前已經轉化過,則把屬性集合設定到BeanWapper中,跳過填充屬性,到初始化Bean。

沒有轉換,迴圈屬性集合PropertyValues pvs,取得值和name屬性,把原始的屬性轉化為Object
RuntimeBeanReference ==>使用getBean方式得到object
BeanDefinitionHolder ===>內部bean解析,有bean工廠建立的object
BeanDefinition        ===>內部bean解析,有bean工廠建立的object
ManagedArray       ===>Object 遞迴迴圈解析,都會呼叫evaluate(Object)
ManagedList        ===>List<Object> 遞迴迴圈解析,都會呼叫evaluate(Object)
ManagedSet         ===>Set<Object> 遞迴迴圈解析,都會呼叫evaluate(Object)
ManagedMap      ===>Map<Object, Object> 遞迴迴圈解析,都會呼叫evaluate(Object)
evaluate(Object)  ===>不管陣列還是集合或map等它們最終都會呼叫這個方法來解析物件,這個方法使用了
BeanExpressionResolver來解析定義的字串可以解析的物件,你可以自己配置一個。spring的EL表示式就是實現了此介面。

解析過的屬性值如果是BeanWrapperImpl可以轉化的就把該屬性轉化為PropertyValue物件。
BeanWrapperImpl擁有TypeConvernt和PropertyEdit介面功能,對屬性操作易如反掌。你可以定製這兩個介面的功能。
所有屬性轉化以後的,,標記轉化後屬性集合為已轉換並填充到BeanWrapperImpl,屬性填充結束。

18.呼叫Aware介面例項的方法
1.如果這個bean實現了BeanNameAware,則呼叫其實現的bean.setBeanName(String beanName)方法,設定在工廠中該bean真實的name。
2.如果這個bean實現了BeanClassLoaderAware,則呼叫其實現的bean.setBeanClassLoader(ClassLoader classLoader)方法,設定載入當前bean的類載入器
3.如果這個bean實現了BeanFactoryAware,則呼叫bean.setBeanFactory(BeanFactory beanFactory);設定當前工廠給bean使用。
spring為什麼要這樣設計?有什麼用?
個人理解:
如果該bean在上下文環境中,我想用當前bean對應的工廠BeanFactory來填充自己的業務方法,或者想用當前工廠的某些儲存的狀態,我並不知道該Bean是那個工廠建立的,或許我要寫很多程式碼才能獲得到beanFactory。現在你只需要實現BeanFactoryAware介面,提供一個setBeanFactory(BeanFactory beanFactory)方法的實現,配置這個bean,spring在初始化時就為你注入該BeanFactory了。BeanFactory別問我能幹什麼?你可以使用 instanceof 來判斷是否是ListableBeanFactory、HierarchicalBeanFactory、SingletonBeanRegistry、ConfigurableBeanFactory,甚至是AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory、BeanDefinitionRegistry中的某個例項,相當於擁有了整個工廠,說不定它還有父工廠,及爺工廠....^_^別問我它們這些介面能幹什麼?可以創無限,只要你可以。

19.初始化之前的後置處理器
   迴圈工廠中所有實現了BeanPostProcessor的例項,呼叫其Object postProcessBeforeInitialization(Object bean, String beanName)方法,該方法允許你包裝引數bean返回一個包裝過的bean物件或者原來這個引數bean物件,如果返回null,後續迴圈的BeanPostProcessor將不會被呼叫。

20.呼叫初始化方法
如果當前的bean是InitializingBean例項,則呼叫該bean的afterPropertiesSet()方法。這個方法用於你可以配置使用上面設定的BeanFactoryAware介面的返回的BeanFactory,或者初始化一些你需要的工作。

21.呼叫配置的初始化方法,如果你在bean定義中設定了init-method,就會使用反射機制呼叫定義的初始化方法。這個和上面的功能差不多,看情況合理使用。

22.初始化完成以後,呼叫後置處理器BeanPostProcessor例項的Object postProcessAfterInitialization(Object bean, String beanName)方法;給你一個已經完全可以使用的bean看你怎麼處理了,或者包裝這個bean或者返回這個bean,這個時候的bean基本已經定型了。如果你返回的object為null,正在迴圈的後置處理器將會跳過。
如果該bean是FactoryBean,將呼叫這個回撥FactoryBean例項和FactoryBean建立的物件。這個處理器可以決定是使用FactoryBean例項還是建立過的物件或者這個兩個。通過返回的物件是否是FactoryBean的例項來檢查。
22.單例曝光物件後續處理
 1.如果新bean是曝光物件,則從池中取出該真實物件引用,如果新的bean物件和之前的放入單例池中的物件是同一例項,也就是經過後置處理器等操作的都是同一例項bean,則把最新bean指向真實的物件應用。
 2.如果已經改變了這個單例bean,則把單例池中的依賴關係解除掉,即從map中清除依賴。
23.註冊銷燬方法
 銷燬方法只能應用於單例bean,所以註冊單例的銷燬方法。
 註冊銷燬方法的條件:
  1.該bean是單例的 
  下面任意一種方式都可以 
  2.如果DisposableBean例項bean
  3.設定的beanDifition的銷燬方法==(inferred)則自動查詢該bean的close方法關閉
  4.設定了銷燬方法
  5.當前工廠存在實現了DestructionAwareBeanPostProcessor介面的後置處理器。
  然後把這些給定的引數設定構造一個DisposableBeanAdapter,放入銷燬登錄檔Map<String, Object> disposableBeans中,快取在該工廠中,
 這不是在這階段執行:我記錄下:
    銷燬單例物件方式:
   1.把單例快取中存在的所有該bean的資訊刪除
   2.把bean在銷燬登錄檔中的銷燬器刪除
   3.銷燬在該工廠中快取的所有bean的依賴物件
   4.如果該bean實現了DisposableBean則呼叫該介面的void destroy(),進行銷燬。
   5.觸發銷燬包含過該bean的單例bean。
   6.刪除其他bean物件對該物件的依賴關係
24.當bean是FactoryBean
判斷工廠是否是FactoryBean例項,如果beanName開頭不是&,並且該bean是BeanFactory的例項。則代表這個bean需要使用BeanFactory的getObject()方法。
如果是不是FactoryBean則直接返回該bean。
判斷得到bean是工廠方法返回物件還是FactoryBean物件:
使用&+beanName訪問的是FactoryBean例項返回的當前bean,否則使用beanName就是工廠方法getObject()返回的物件。
否則該bean就是FactoyrBean,就呼叫其例項實現介面的getObject()返回的bean物件。