javascript
Spring框架—SpringBean加载过程
原文作者:RunAlgorithm
原文地址:圖文并茂,揭秘 Spring 的 Bean 的加載過程
?
1. 概述
Spring 作為 Ioc 框架,實現(xiàn)了依賴注入,由一個中心化的 Bean 工廠來負(fù)責(zé)各個 Bean 的實例化和依賴管理。各個 Bean 可以不需要關(guān)心各自的復(fù)雜的創(chuàng)建過程,達(dá)到了很好的解耦效果。我們對 Spring 的工作流進行一個粗略的概括,主要為兩大環(huán)節(jié):
- 解析:讀 xml 配置,掃描類文件,從配置或者注解中獲取 Bean 的定義信息,注冊一些擴展功能。
- 加載:通過解析完的定義信息獲取 Bean 實例。
?
我們假設(shè)所有的配置和擴展類都已經(jīng)裝載到了 ApplicationContext 中,然后具體的分析一下 Bean 的加載流程。思考一個問題,拋開 Spring 框架的實現(xiàn),假設(shè)我們手頭上已經(jīng)有一套完整的 Bean Definition Map,然后指定一個 beanName 要進行實例化,需要關(guān)心什么?即使我們沒有 Spring 框架,也需要了解這兩方面的知識:
- 作用域:單例作用域或者原型作用域,單例的話需要全局實例化一次,原型每次創(chuàng)建都需要重新實例化。
- 依賴關(guān)系:一個 Bean 如果有依賴,我們需要初始化依賴,然后進行關(guān)聯(lián)。如果多個 Bean 之間存在著循環(huán)依賴,A 依賴 B,B 依賴 C,C 又依賴 A,需要解這種循環(huán)依賴問題。
Spring 進行了抽象和封裝,使得作用域和依賴關(guān)系的配置對開發(fā)者透明,我們只需要知道當(dāng)初在配置里已經(jīng)明確指定了它的生命周期和依賴了誰,至于是怎么實現(xiàn)的,依賴如何注入,托付給了 Spring 工廠來管理。Spring 只暴露了很簡單的接口給調(diào)用者,比如 getBean :
ApplicationContext context = new ClassPathXmlApplicationContext("hello.xml"); HelloBean helloBean = (HelloBean) context.getBean("hello"); helloBean.sayHello();那我們就從 getBean 方法作為入口,去理解 Spring 加載的流程是怎樣的,以及內(nèi)部對創(chuàng)建信息、作用域、依賴關(guān)系等等的處理細(xì)節(jié)。
2. 總體流程
?
Bean 加載流程圖上面是跟蹤了 getBean 的調(diào)用鏈創(chuàng)建的流程圖,為了能夠很好地理解 Bean 加載流程,省略一些異常、日志和分支處理和一些特殊條件的判斷。從上面的流程圖中,可以看到一個 Bean 加載會經(jīng)歷這么幾個階段(用綠色標(biāo)記):
- 獲取 BeanName:對傳入的 name 進行解析,轉(zhuǎn)化為可以從 Map 中獲取到 BeanDefinition 的 bean name。
- 合并 Bean 定義:對父類的定義進行合并和覆蓋,如果父類還有父類,會進行遞歸合并,以獲取完整的 Bean 定義信息。
- 實例化:使用構(gòu)造或者工廠方法創(chuàng)建 Bean 實例。
- 屬性填充:尋找并且注入依賴,依賴的 Bean 還會遞歸調(diào)用 getBean 方法獲取。
- 初始化:調(diào)用自定義的初始化方法。
- 獲取最終的 Bean:如果是 FactoryBean 需要調(diào)用 getObject 方法,如果需要類型轉(zhuǎn)換調(diào)用 TypeConverter 進行轉(zhuǎn)化。
整個流程最為復(fù)雜的是對循環(huán)依賴的解決方案,后續(xù)會進行重點分析。
3. 細(xì)節(jié)分析
3.1. 轉(zhuǎn)化 BeanName
而在我們解析完配置后創(chuàng)建的 Map,使用的是 beanName 作為 key。見 DefaultListableBeanFactory:
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);BeanFactory.getBean 中傳入的 name,有可能是這幾種情況:
- bean name:可以直接獲取到定義 BeanDefinition。
- alias name:別名,需要轉(zhuǎn)化。
- factorybean name:?帶 & 前綴,通過它獲取 BeanDefinition 的時候需要去除 & 前綴。
為了能夠獲取到正確的 BeanDefinition,需要先對 name 做一個轉(zhuǎn)換,得到 beanName。
name轉(zhuǎn)beanName?
?
見 AbstractBeanFactory.doGetBean:
protected <T> T doGetBean ... {...// 轉(zhuǎn)化工作 final String beanName = transformedBeanName(name);... }如果是 alias name,在解析階段,alias name 和 bean name 的映射關(guān)系被注冊到 SimpleAliasRegistry 中。從該注冊器中取到 beanName。見 SimpleAliasRegistry.canonicalName:
public String canonicalName(String name) {...resolvedName = this.aliasMap.get(canonicalName);... }如果是 factorybean name,表示這是個工廠 bean,有攜帶前綴修飾符 & 的,直接把前綴去掉。見 BeanFactoryUtils.transformedBeanName :
public static String transformedBeanName(String name) {Assert.notNull(name, "'name' must not be null");String beanName = name;while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());}return beanName; }3.2. 合并 RootBeanDefinition
我們從配置文件讀取到的 BeanDefinition 是 GenericBeanDefinition。它記錄了一些當(dāng)前類聲明的屬性或構(gòu)造參數(shù),但是對于父類只用了一個 parentName 來記錄。
public class GenericBeanDefinition extends AbstractBeanDefinition {...private String parentName;... }接下來會發(fā)現(xiàn)一個問題,在后續(xù)實例化 Bean 的時候,使用的 BeanDefinition 是 RootBeanDefinition 類型而非 GenericBeanDefinition。這是為什么?答案很明顯,GenericBeanDefinition 在有繼承關(guān)系的情況下,定義的信息不足:
- 如果不存在繼承關(guān)系,GenericBeanDefinition 存儲的信息是完整的,可以直接轉(zhuǎn)化為 RootBeanDefinition。
- 如果存在繼承關(guān)系,GenericBeanDefinition 存儲的是 增量信息 而不是 全量信息。
為了能夠正確初始化對象,需要完整的信息才行。需要遞歸 合并父類的定義:
合并BeanDefinition?
見 AbstractBeanFactory.doGetBean :
protected <T> T doGetBean ... {...// 合并父類定義final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);...// 使用合并后的定義進行實例化bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);... }在判斷 parentName 存在的情況下,說明存在父類定義,啟動合并。如果父類還有父類怎么辦?遞歸調(diào)用,繼續(xù)合并。見AbstractBeanFactory.getMergedBeanDefinition 方法:
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, BeanDefinition containingBd)throws BeanDefinitionStoreException {...String parentBeanName = transformedBeanName(bd.getParentName());...// 遞歸調(diào)用,繼續(xù)合并父類定義pbd = getMergedBeanDefinition(parentBeanName);...// 使用合并后的完整定義,創(chuàng)建 RootBeanDefinitionmbd = new RootBeanDefinition(pbd);// 使用當(dāng)前定義,對 RootBeanDefinition 進行覆蓋mbd.overrideFrom(bd);...return mbd;}每次合并完父類定義后,都會調(diào)用 RootBeanDefinition.overrideFrom 對父類的定義進行覆蓋,獲取到當(dāng)前類能夠正確實例化的 全量信息。
3.3. 處理循環(huán)依賴
什么是循環(huán)依賴?舉個例子,這里有三個類 A、B、C,然后 A 關(guān)聯(lián) B,B 關(guān)聯(lián) C,C 又關(guān)聯(lián) A,這就形成了一個循環(huán)依賴。如果是方法調(diào)用是不算循環(huán)依賴的,循環(huán)依賴必須要持有引用。
循環(huán)依賴?
循環(huán)依賴根據(jù)注入的時機分成兩種類型:
- 構(gòu)造器循環(huán)依賴:依賴的對象是通過構(gòu)造器傳入的,發(fā)生在實例化 Bean 的時候。這種依賴本質(zhì)上是無法解決的。比如我們準(zhǔn)調(diào)用 A 的構(gòu)造器,發(fā)現(xiàn)依賴 B,于是去調(diào)用 B 的構(gòu)造器進行實例化,發(fā)現(xiàn)又依賴 C,于是調(diào)用 C 的構(gòu)造器去初始化,結(jié)果依賴 A,整個形成一個死結(jié),導(dǎo)致 A 無法創(chuàng)建。
- 設(shè)值循環(huán)依賴:依賴的對象是通過 setter 方法傳入的,對象已經(jīng)實例化,發(fā)生屬性填充和依賴注入的時候。Spring 框架只支持單例下的設(shè)值循環(huán)依賴。Spring 通過對還在創(chuàng)建過程中的單例,緩存并提前暴露該單例,使得其他實例可以引用該依賴。
3.3.1. 原型模式的循環(huán)依賴
Spring 不支持原型模式的任何循環(huán)依賴。檢測到循環(huán)依賴會直接拋出 BeanCurrentlyInCreationException 異常。使用了一個 ThreadLocal 變量 prototypesCurrentlyInCreation 來記錄當(dāng)前線程正在創(chuàng)建中的 Bean 對象,見 AbtractBeanFactory#prototypesCurrentlyInCreation:
/** Names of beans that are currently in creation */ private final ThreadLocal<Object> prototypesCurrentlyInCreation =new NamedThreadLocal<Object>("Prototype beans currently in creation");在 Bean 創(chuàng)建前進行記錄,在 Bean 創(chuàng)建后刪除記錄。見 AbstractBeanFactory.doGetBean:
... if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {// 添加記錄beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {// 刪除記錄afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } ...見 AbtractBeanFactory.beforePrototypeCreation 的記錄操作:
protected void beforePrototypeCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();if (curVal == null) {this.prototypesCurrentlyInCreation.set(beanName);}else if (curVal instanceof String) {Set<String> beanNameSet = new HashSet<String>(2);beanNameSet.add((String) curVal);beanNameSet.add(beanName);this.prototypesCurrentlyInCreation.set(beanNameSet);}else {Set<String> beanNameSet = (Set<String>) curVal;beanNameSet.add(beanName);}}見 AbtractBeanFactory.beforePrototypeCreation 的刪除操作:
protected void afterPrototypeCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();if (curVal instanceof String) {this.prototypesCurrentlyInCreation.remove();}else if (curVal instanceof Set) {Set<String> beanNameSet = (Set<String>) curVal;beanNameSet.remove(beanName);if (beanNameSet.isEmpty()) {this.prototypesCurrentlyInCreation.remove();}}}為了節(jié)省內(nèi)存空間,在單個元素時 prototypesCurrentlyInCreation 只記錄 String 對象,在多個依賴元素后改用 Set 集合。這里是 Spring 使用的一個節(jié)約內(nèi)存的小技巧。了解了記錄的寫入和刪除過程好了,再來看看讀取以及判斷循環(huán)的方式。這里要分兩種情況討論。
- 構(gòu)造函數(shù)循環(huán)依賴。
- 設(shè)置循環(huán)依賴。
這兩個地方的實現(xiàn)略有不同。如果是構(gòu)造函數(shù)依賴的,比如 A 的構(gòu)造函數(shù)依賴了 B,會有這樣的情況。實例化 A 的階段中,匹配到要使用的構(gòu)造函數(shù),發(fā)現(xiàn)構(gòu)造函數(shù)有參數(shù) B,會使用 BeanDefinitionValueResolver 來檢索 B 的實例。見 BeanDefinitionValueResolver.resolveReference:
private Object resolveReference(Object argName, RuntimeBeanReference ref) {...Object bean = this.beanFactory.getBean(refName);... }我們發(fā)現(xiàn)這里繼續(xù)調(diào)用 beanFactory.getBean 去加載 B。如果是設(shè)值循環(huán)依賴的的,比如我們這里不提供構(gòu)造函數(shù),并且使用了 @Autowire 的方式注解依賴(還有其他方式不舉例了):
public class A {@Autowiredprivate B b;... }加載過程中,找到無參數(shù)構(gòu)造函數(shù),不需要檢索構(gòu)造參數(shù)的引用,實例化成功。接著執(zhí)行下去,進入到屬性填充階段 AbtractBeanFactory.populateBean ,在這里會進行 B 的依賴注入。為了能夠獲取到 B 的實例化后的引用,最終會通過檢索類 DependencyDescriptor 中去把依賴讀取出來,見 DependencyDescriptor.resolveCandidate :
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)throws BeansException {return beanFactory.getBean(beanName, requiredType); }發(fā)現(xiàn) beanFactory.getBean 方法又被調(diào)用到了。在這里,兩種循環(huán)依賴達(dá)成了同一。無論是構(gòu)造函數(shù)的循環(huán)依賴還是設(shè)置循環(huán)依賴,在需要注入依賴的對象時,會繼續(xù)調(diào)用 beanFactory.getBean 去加載對象,形成一個遞歸操作。而每次調(diào)用 beanFactory.getBean 進行實例化前后,都使用了 prototypesCurrentlyInCreation 這個變量做記錄。按照這里的思路走,整體效果等同于 建立依賴對象的構(gòu)造鏈。prototypesCurrentlyInCreation 中的值的變化如下:
原型模式的循環(huán)依賴?
調(diào)用判定的地方在 AbstractBeanFactory.doGetBean 中,所有對象的實例化均會從這里啟動。
// Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName); }判定的實現(xiàn)方法為 AbstractBeanFactory.isPrototypeCurrentlyInCreation :
protected boolean isPrototypeCurrentlyInCreation(String beanName) {Object curVal = this.prototypesCurrentlyInCreation.get();return (curVal != null &&(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }所以在原型模式下,構(gòu)造函數(shù)循環(huán)依賴和設(shè)值循環(huán)依賴,本質(zhì)上使用同一種方式檢測出來。Spring 無法解決,直接拋出 BeanCurrentlyInCreationException 異常。
3.3.2. 單例模式的構(gòu)造循環(huán)依賴
Spring 也不支持單例模式的構(gòu)造循環(huán)依賴。檢測到構(gòu)造循環(huán)依賴也會拋出 BeanCurrentlyInCreationException 異常。和原型模式相似,單例模式也用了一個數(shù)據(jù)結(jié)構(gòu)來記錄正在創(chuàng)建中的 beanName。見 DefaultSingletonBeanRegistry:
/** Names of beans that are currently in creation */ private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));會在創(chuàng)建前進行記錄,創(chuàng)建化后刪除記錄。見 DefaultSingletonBeanRegistry.getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {...// 記錄正在加載中的 beanNamebeforeSingletonCreation(beanName);...// 通過 singletonFactory 創(chuàng)建 beansingletonObject = singletonFactory.getObject();...// 刪除正在加載中的 beanNameafterSingletonCreation(beanName);}記錄和判定的方式見 DefaultSingletonBeanRegistry.beforeSingletonCreation :
protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}}這里會嘗試往 singletonsCurrentlyInCreation 記錄當(dāng)前實例化的 bean。我們知道 singletonsCurrentlyInCreation 的數(shù)據(jù)結(jié)構(gòu)是 Set,是不允許重復(fù)元素的,所以一旦前面記錄了,這里的 add 操作將會返回失敗。比如加載 A 的單例,和原型模式類似,單例模式也會調(diào)用匹配到要使用的構(gòu)造函數(shù),發(fā)現(xiàn)構(gòu)造函數(shù)有參數(shù) B,然后使用 BeanDefinitionValueResolver 來檢索 B 的實例,根據(jù)上面的分析,繼續(xù)調(diào)用 beanFactory.getBean 方法。所以拿 A,B,C 的例子來舉例 singletonsCurrentlyInCreation 的變化,這里可以看到和原型模式的循環(huán)依賴判斷方式的算法是一樣:
單例模式的構(gòu)造循環(huán)依賴?
- 加載 A。記錄 singletonsCurrentlyInCreation = [a],構(gòu)造依賴 B,開始加載 B。
- 加載 B,記錄 singletonsCurrentlyInCreation = [a, b],構(gòu)造依賴 C,開始加載 C。
- 加載 C,記錄 singletonsCurrentlyInCreation = [a, b, c],構(gòu)造依賴 A,又開始加載 A。
- 加載 A,執(zhí)行到 DefaultSingletonBeanRegistry.beforeSingletonCreation ,singletonsCurrentlyInCreation 中 a 已經(jīng)存在了,檢測到構(gòu)造循環(huán)依賴,直接拋出異常結(jié)束操作。
3.3.3. 單例模式的設(shè)值循環(huán)依賴
單例模式下,構(gòu)造函數(shù)的循環(huán)依賴無法解決,但設(shè)值循環(huán)依賴是可以解決的。這里有一個重要的設(shè)計:提前暴露創(chuàng)建中的單例。我們理解一下為什么要這么做。還是拿上面的 A、B、C 的的設(shè)值依賴做分析,
- => 1. A 創(chuàng)建 -> A 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 B,啟動 B 的實例化
- => 2. B 創(chuàng)建 -> B 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 C,啟動 C 的實例化
- => 3. C 創(chuàng)建 -> C 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 A
重點來了,在我們的階段 1中, A 已經(jīng)構(gòu)造完成,Bean 對象在堆中也分配好內(nèi)存了,即使后續(xù)往 A 中填充屬性(比如填充依賴的 B 對象),也不會修改到 A 的引用地址。所以,這個時候是否可以 提前拿 A 實例的引用來先注入到 C ,去完成 C 的實例化,于是流程變成這樣。
- => 3. C 創(chuàng)建 -> C 構(gòu)造完成,開始注入依賴,發(fā)現(xiàn)依賴 A,發(fā)現(xiàn) A 已經(jīng)構(gòu)造完成,直接引用,完成 C 的實例化。
- => 4. C 完成實例化后,B 注入 C 也完成實例化,A 注入 B 也完成實例化。
這就是 Spring 解決單例模式設(shè)值循環(huán)依賴應(yīng)用的技巧。流程圖為:
單例模式創(chuàng)建流程?
為了能夠?qū)崿F(xiàn)單例的提前暴露。Spring 使用了三級緩存,見 DefaultSingletonBeanRegistry:
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);/** Cache of singleton factories: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);/** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);這三個緩存的區(qū)別如下:
- singletonObjects,單例緩存,存儲已經(jīng)實例化完成的單例。
- singletonFactories,生產(chǎn)單例的工廠的緩存,存儲工廠。
- earlySingletonObjects,提前暴露的單例緩存,這時候的單例剛剛創(chuàng)建完,但還會注入依賴。
從 getBean("a") 開始,添加的 SingletonFactory 具體實現(xiàn)如下:
protected Object doCreateBean ... {...addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});... }可以看到如果使用該 SingletonFactory 獲取實例,使用的是 getEarlyBeanReference 方法,返回一個未初始化的引用。讀取緩存的地方見 DefaultSingletonBeanRegistry :
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return (singletonObject != NULL_OBJECT ? singletonObject : null); }先嘗試從 singletonObjects 和 singletonFactory 讀取,沒有數(shù)據(jù),然后嘗試 singletonFactories 讀取 singletonFactory,執(zhí)行 getEarlyBeanReference 獲取到引用后,存儲到 earlySingletonObjects 中。這個 earlySingletonObjects 的好處是,如果此時又有其他地方嘗試獲取未初始化的單例,可以從 earlySingletonObjects 直接取出而不需要再調(diào)用 getEarlyBeanReference。從流程圖上看,實際上注入 C 的 A 實例,還在填充屬性階段,并沒有完全地初始化。等遞歸回溯回去,A 順利拿到依賴 B,才會真實地完成 A 的加載。
3.4. 創(chuàng)建實例
獲取到完整的 RootBeanDefintion 后,就可以拿這份定義信息來實例具體的 Bean。具體實例創(chuàng)建見 AbstractAutowireCapableBeanFactory.createBeanInstance ,返回 Bean 的包裝類 BeanWrapper,一共有三種策略:
- 使用工廠方法創(chuàng)建,instantiateUsingFactoryMethod 。
- 使用有參構(gòu)造函數(shù)創(chuàng)建,autowireConstructor。
- 使用無參構(gòu)造函數(shù)創(chuàng)建,instantiateBean。
使用工廠方法創(chuàng)建,會先使用 getBean 獲取工廠類,然后通過參數(shù)找到匹配的工廠方法,調(diào)用實例化方法實現(xiàn)實例化,具體見ConstructorResolver.instantiateUsingFactoryMethod :
public BeanWrapper instantiateUsingFactoryMethod ... (...String factoryBeanName = mbd.getFactoryBeanName();...factoryBean = this.beanFactory.getBean(factoryBeanName);...// 匹配正確的工廠方法...beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(...);...bw.setBeanInstance(beanInstance);return bw; }使用有參構(gòu)造函數(shù)創(chuàng)建,整個過程比較復(fù)雜,涉及到參數(shù)和構(gòu)造器的匹配。為了找到匹配的構(gòu)造器,Spring 花了大量的工作,見 ConstructorResolver.autowireConstructor :
public BeanWrapper autowireConstructor ... {...Constructor<?> constructorToUse = null;...// 匹配構(gòu)造函數(shù)的過程...beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(...);...bw.setBeanInstance(beanInstance);return bw; }使用無參構(gòu)造函數(shù)創(chuàng)建是最簡單的方式,見 AbstractAutowireCapableBeanFactory.instantiateBean:
protected BeanWrapper instantiateBean ... {...beanInstance = getInstantiationStrategy().instantiate(...);...BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;... }我們發(fā)現(xiàn)這三個實例化方式,最后都會走 getInstantiationStrategy().instantiate(...),見實現(xiàn)類 SimpleInstantiationStrategy.instantiate:
public Object instantiate ... {if (bd.getMethodOverrides().isEmpty()) {...return BeanUtils.instantiateClass(constructorToUse);}else {// Must generate CGLIB subclass.return instantiateWithMethodInjection(bd, beanName, owner);} }雖然拿到了構(gòu)造函數(shù),并沒有立即實例化。因為用戶使用了 replace 和 lookup 的配置方法,用到了動態(tài)代理加入對應(yīng)的邏輯。如果沒有的話,直接使用反射來創(chuàng)建實例。創(chuàng)建實例后,就可以開始注入屬性和初始化等操作。但這里的 Bean 還不是最終的 Bean。返回給調(diào)用方使用時,如果是 FactoryBean 的話需要使用 getObject 方法來創(chuàng)建實例。見 AbstractBeanFactory.getObjectFromBeanInstance ,會執(zhí)行到 doGetObjectFromFactoryBean :
private Object doGetObjectFromFactoryBean ... {...object = factory.getObject();...return object; }3.5. 注入屬性
實例創(chuàng)建完后開始進行屬性的注入,如果涉及到外部依賴的實例,會自動檢索并關(guān)聯(lián)到該當(dāng)前實例。Ioc 思想體現(xiàn)出來了。正是有了這一步操作,Spring 降低了各個類之間的耦合。屬性填充的入口方法在AbstractAutowireCapableBeanFactory.populateBean。
protected void populateBean ... {PropertyValues pvs = mbd.getPropertyValues();...// InstantiationAwareBeanPostProcessor 前處理for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}...// 根據(jù)名稱注入if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// 根據(jù)類型注入if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}... // InstantiationAwareBeanPostProcessor 后處理for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvs == null) {return;}}}...// 應(yīng)用屬性值applyPropertyValues(beanName, mbd, bw, pvs); }可以看到主要的處理環(huán)節(jié)有:
- 應(yīng)用 InstantiationAwareBeanPostProcessor 處理器,在屬性注入前后進行處理。假設(shè)我們使用了 @Autowire 注解,這里會調(diào)用到 AutowiredAnnotationBeanPostProcessor 來對依賴的實例進行檢索和注入的,它是 InstantiationAwareBeanPostProcessor 的子類。
- 根據(jù)名稱或者類型進行自動注入,存儲結(jié)果到 PropertyValues 中。
- 應(yīng)用 PropertyValues,填充到 BeanWrapper。這里在檢索依賴實例的引用的時候,會遞歸調(diào)用 BeanFactory.getBean 來獲得。
3.6. 初始化
3.6.1. 觸發(fā) Aware
如果我們的 Bean 需要容器的一些資源該怎么辦?比如需要獲取到 BeanFactory、ApplicationContext 等等。Spring 提供了 Aware 系列接口來解決這個問題。比如有這樣的 Aware:
- BeanFactoryAware,用來獲取 BeanFactory。
- ApplicationContextAware,用來獲取 ApplicationContext。
- ResourceLoaderAware,用來獲取 ResourceLoaderAware。
- ServletContextAware,用來獲取 ServletContext。
Spring 在初始化階段,如果判斷 Bean 實現(xiàn)了這幾個接口之一,就會往 Bean 中注入它關(guān)心的資源。見 AbstractAutowireCapableBeanFactory.invokeAwareMethos :
private void invokeAwareMethods(final String beanName, final Object bean) {if (bean instanceof Aware) {if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName(beanName);}if (bean instanceof BeanClassLoaderAware) {((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());}if (bean instanceof BeanFactoryAware) {((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}} }3.6.2. 觸發(fā) BeanPostProcessor
在 Bean 的初始化前或者初始化后,我們?nèi)绻枰M行一些增強操作怎么辦?這些增強操作比如打日志、做校驗、屬性修改、耗時檢測等等。Spring 框架提供了 BeanPostProcessor 來達(dá)成這個目標(biāo)。比如我們使用注解 @Autowire 來聲明依賴,就是使用 AutowiredAnnotationBeanPostProcessor 來實現(xiàn)依賴的查詢和注入的。接口定義如下:
public interface BeanPostProcessor {// 初始化前調(diào)用Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;// 初始化后調(diào)用Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;}實現(xiàn)該接口的 Bean 都會被 Spring 注冊到 beanPostProcessors 中,見 AbstractBeanFactory :
/** BeanPostProcessors to apply in createBean */ private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();只要 Bean 實現(xiàn)了 BeanPostProcessor 接口,加載的時候會被 Spring 自動識別這些 Bean,自動注冊,非常方便。然后在 Bean 實例化前后,Spring 會去調(diào)用我們已經(jīng)注冊的 beanPostProcessors 把處理器都執(zhí)行一遍。
public abstract class AbstractAutowireCapableBeanFactory ... {...@Overridepublic Object applyBeanPostProcessorsBeforeInitialization ... {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessBeforeInitialization(result, beanName);if (result == null) {return result;}}return result;}@Overridepublic Object applyBeanPostProcessorsAfterInitialization ... {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessAfterInitialization(result, beanName);if (result == null) {return result;}}return result;}... }這里使用了責(zé)任鏈模式,Bean 會在處理器鏈中進行傳遞和處理。當(dāng)我們調(diào)用 BeanFactory.getBean 的后,執(zhí)行到 Bean 的初始化方法 AbstractAutowireCapableBeanFactory.initializeBean 會啟動這些處理器。
protected Object initializeBean ... { ...wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);...// 觸發(fā)自定義 init 方法invokeInitMethods(beanName, wrappedBean, mbd);...wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);... }3.6.3. 觸發(fā)自定義 init
自定義初始化有兩種方式可以選擇:
- 實現(xiàn) InitializingBean。提供了一個很好的機會,在屬性設(shè)置完成后再加入自己的初始化邏輯。
- 定義 init 方法。自定義的初始化邏輯。
見 AbstractAutowireCapableBeanFactory.invokeInitMethods :
protected void invokeInitMethods ... {boolean isInitializingBean = (bean instanceof InitializingBean);if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {...((InitializingBean) bean).afterPropertiesSet();...}if (mbd != null) {String initMethodName = mbd.getInitMethodName();if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {invokeCustomInitMethod(beanName, bean, mbd);}}}3.7. 類型轉(zhuǎn)換
Bean 已經(jīng)加載完畢,屬性也填充好了,初始化也完成了。在返回給調(diào)用者之前,還留有一個機會對 Bean 實例進行類型的轉(zhuǎn)換。見 AbstractBeanFactory.doGetBean :
protected <T> T doGetBean ... {...if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {...return getTypeConverter().convertIfNecessary(bean, requiredType);...}return (T) bean; }4. 總結(jié)
拋開一些細(xì)節(jié)處理和擴展功能,一個 Bean 的創(chuàng)建過程無非是:獲取完整定義 -> 實例化 -> 依賴注入 -> 初始化 -> 類型轉(zhuǎn)換。
作為一個完善的框架,Spring 需要考慮到各種可能性,還需要考慮到接入的擴展性。所以有了復(fù)雜的循環(huán)依賴的解決,復(fù)雜的有參數(shù)構(gòu)造器的匹配過程,有了 BeanPostProcessor 來對實例化或初始化的 Bean 進行擴展修改。
先有個整體設(shè)計的思維,再逐步擊破針對這些特殊場景的設(shè)計,整個 Bean 加載流程迎刃而解。
總結(jié)
以上是生活随笔為你收集整理的Spring框架—SpringBean加载过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring框架—SpringBean配
- 下一篇: Spring框架—SpringBean源