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對象。
GameOver!!!好累!!