- BeanDefinition
1.1什么是BeanDefinition?
- BeanDefinition表示Bean定義,
- Spring根據(jù)BeanDefiniton來(lái)創(chuàng)建Bean對(duì)象,
- BeanDefinition有很多的屬性用來(lái)描述Bean,
- BeanDefinition是Spring中非常核心的概念。
1.2 BeanDifinition中重要的屬性
- beanClass:表示一個(gè)Bean的類型,比如:UserService.class、OrderService.class,Spring在創(chuàng)建Bean的過(guò)程中會(huì)根據(jù)此屬性來(lái)實(shí)例化得到對(duì)象;
- scope:表示一個(gè)bean的作用域,比如:
scope等于singleton,該bean就是一個(gè)單例Bean;
scope等于prototype,該Bean就是一個(gè)原型Bean。
- isLazy:表示一個(gè)Bean是不是需要懶加載,原型Bean的isLazy屬性不起作用,懶加載的單例Bean,會(huì)在第一次getBean的時(shí)候生成該Bean,非懶加載的單例Bean,則會(huì)在Spring啟動(dòng)過(guò)程中直接生成好。
- dependsOn:表示一個(gè)Bean在創(chuàng)建之前所依賴的其他Bean,在一個(gè)Bean創(chuàng)建之前,它所依賴的Bean得先全部創(chuàng)建好。
- primary:表示一個(gè)Bean是主Bean,在Spring中一個(gè)類型可以有多個(gè)Bean對(duì)象,在進(jìn)行依賴注入時(shí),如果根據(jù)類型找到了多個(gè)Bean,此時(shí)會(huì)判斷這些Bean中是否存在一個(gè)主Bean,如果存在,則直接將這個(gè)Bean注入給屬性。
- initMethodName:表示一個(gè)Bean的初始化方法,一個(gè)Bean的生命周期過(guò)程中有一個(gè)步驟叫初始化,Spring會(huì)在這個(gè)步驟中去調(diào)用Bean的初始化方法初始化邏輯由程序員自己控制,表示程序員可以自己自定義邏輯對(duì)Bean進(jìn)行加工。
1.3 BeanDefinition的解析
- BeanFactory
2.1 什么是BeanFactory?
- BeanFactory是一種“Spring容器”,
- BeanFactory翻譯過(guò)來(lái)就是Bean工廠,
- 顧名思義,它可以用來(lái)創(chuàng)建Bean、獲取Bean,
- BeanFactory是Spring中非常核心的組件。
2.2 BeanFactory工作流程:
BeanFactory將利用BeanDefinition來(lái)生成Bean對(duì)象,
BeanDefinition相當(dāng)于BeanFactory的原材料,
Bean對(duì)象就相當(dāng)于BeanFactory所生產(chǎn)出來(lái)的產(chǎn)品。
- Bean生命周期
3.1 什么是Bean生命周期?
Bean生命周期是Spring中一個(gè)Bean創(chuàng)建過(guò)程和銷毀過(guò)程中所經(jīng)歷的步驟,其中Bean創(chuàng)建過(guò)程是重點(diǎn)。
程序員可以利用Bean生命周期機(jī)制對(duì)Bean進(jìn)行自定義加工。
3.2 核心步驟
- BeanDefinition: BeanDefinition表示Bean定義,它定義了某個(gè)Bean的類型,Spring就是利用BeanDefinition來(lái)創(chuàng)建Bean的,比如需要BeanDefinition中beanClass屬性確定Bean的類型,從而實(shí)例化出來(lái)對(duì)象。
- 構(gòu)造方法判斷: 一個(gè)Bean中可以由多個(gè)構(gòu)造方法,此時(shí)就需要Spring來(lái)判斷到底使用哪個(gè)構(gòu)造方法,這個(gè)過(guò)程是比較復(fù)雜的。通過(guò)構(gòu)造方法推斷之后確定一個(gè)構(gòu)造方法后,就可以利用構(gòu)造方法實(shí)例化得到一個(gè)對(duì)象了。
- 實(shí)例化: 通過(guò)構(gòu)造方法反射得到一個(gè)實(shí)例化對(duì)象,在Spring中,可以通過(guò)BeanPostProcessor機(jī)制對(duì)實(shí)例化進(jìn)行干預(yù)。
- 屬性填充: 實(shí)例化所得到的對(duì)象,是“不完整”的對(duì)象,“不完整”的意思是該對(duì)象中的某些屬性還沒有進(jìn)行屬性填充,也就是Spring還沒有自動(dòng)給某些屬性賦值,屬性填充就是我們通常說(shuō)的自動(dòng)注入、依賴注入。
- 初始化: 在一個(gè)對(duì)象的屬性填充之后,Spring提供了初始化機(jī)制,程序員可以利用初始化機(jī)制來(lái)對(duì)Bean進(jìn)行自定義加工,比如可以利用InitializingBean接口來(lái)對(duì)Bean中的其他屬性進(jìn)行賦值,或?qū)ean中的某些屬性進(jìn)行校驗(yàn)。
- 初始化后: 初始化后是Bean創(chuàng)建生命周期中最后一個(gè)步驟,我們常說(shuō)的AOP機(jī)制,就是在這個(gè)步驟中通過(guò)BeanPostProcessor機(jī)制實(shí)現(xiàn)的,初始化之后得到的對(duì)象才是真正的Bean對(duì)象。
詳細(xì)流程:
上面是跟蹤了 getBean 的調(diào)用鏈創(chuàng)建的流程圖,為了能夠很好地理解 Bean 加載流程,省略一些異常、日志和分支處理和一些特殊條件的判斷。
從上面的流程圖中,可以看到一個(gè) Bean 加載會(huì)經(jīng)歷這么幾個(gè)階段(用綠色標(biāo)記):
- 獲取 BeanName,對(duì)傳入的 name 進(jìn)行解析,轉(zhuǎn)化為可以從 Map 中獲取到 BeanDefinition 的 bean name。
- 合并 Bean 定義,對(duì)父類的定義進(jìn)行合并和覆蓋,如果父類還有父類,會(huì)進(jìn)行遞歸合并,以獲取完整的 Bean 定義信息。
- 實(shí)例化,使用構(gòu)造或者工廠方法創(chuàng)建 Bean 實(shí)例。
- 屬性填充,尋找并且注入依賴,依賴的 Bean 還會(huì)遞歸調(diào)用 getBean 方法獲取。
- 初始化,調(diào)用自定義的初始化方法。
- 獲取最終的 Bean,如果是 FactoryBean 需要調(diào)用 getObject 方法,如果需要類型轉(zhuǎn)換調(diào)用 TypeConverter 進(jìn)行轉(zhuǎn)化。
整個(gè)流程最為復(fù)雜的是對(duì)循環(huán)依賴的解決方案,后續(xù)會(huì)進(jìn)行重點(diǎn)分析。
- 細(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, 帶 & 前綴,通過(guò)它獲取 BeanDefinition 的時(shí)候需要去除 & 前綴。
為了能夠獲取到正確的 BeanDefinition,需要先對(duì) name 做一個(gè)轉(zhuǎn)換,得到 beanName。
name轉(zhuǎn)beanName
見 AbstractBeanFactory.doGetBean:
protected T doGetBean … {
…
// 轉(zhuǎn)化工作
final String beanName = transformedBeanName(name);
...
}
如果是 alias name,在解析階段,alias name 和 bean name 的映射關(guān)系被注冊(cè)到 SimpleAliasRegistry 中。從該注冊(cè)器中取到 beanName。見 SimpleAliasRegistry.canonicalName:
public String canonicalName(String name) {
…
resolvedName = this.aliasMap.get(canonicalName);
…
}
如果是 factorybean name,表示這是個(gè)工廠 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ù),但是對(duì)于父類只用了一個(gè) parentName 來(lái)記錄。
public class GenericBeanDefinition extends AbstractBeanDefinition {
…
private String parentName;
…
}
接下來(lái)會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,在后續(xù)實(shí)例化 Bean 的時(shí)候,使用的 BeanDefinition 是 RootBeanDefinition 類型而非 GenericBeanDefinition。這是為什么?
答案很明顯,GenericBeanDefinition 在有繼承關(guān)系的情況下,定義的信息不足:
如果不存在繼承關(guān)系,GenericBeanDefinition 存儲(chǔ)的信息是完整的,可以直接轉(zhuǎn)化為 RootBeanDefinition。
如果存在繼承關(guān)系,GenericBeanDefinition 存儲(chǔ)的是 增量信息 而不是 全量信息。
為了能夠正確初始化對(duì)象,需要完整的信息才行。需要遞歸 合并父類的定義:
合并BeanDefinition
見 AbstractBeanFactory.doGetBean :
protected T doGetBean … {
…
// 合并父類定義
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
...
// 使用合并后的定義進(jìn)行實(shí)例化
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
...
}
在判斷 parentName 存在的情況下,說(shuō)明存在父類定義,啟動(dòng)合并。如果父類還有父類怎么辦?遞歸調(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)建 RootBeanDefinition
mbd = new RootBeanDefinition(pbd);
// 使用當(dāng)前定義,對(duì) RootBeanDefinition 進(jìn)行覆蓋
mbd.overrideFrom(bd);
...
return mbd;
}
每次合并完父類定義后,都會(huì)調(diào)用 RootBeanDefinition.overrideFrom 對(duì)父類的定義進(jìn)行覆蓋,獲取到當(dāng)前類能夠正確實(shí)例化的 全量信息。
3.3. 處理循環(huán)依賴
什么是循環(huán)依賴?
舉個(gè)例子,這里有三個(gè)類 A、B、C,然后 A 關(guān)聯(lián) B,B 關(guān)聯(lián) C,C 又關(guān)聯(lián) A,這就形成了一個(gè)循環(huán)依賴。如果是方法調(diào)用是不算循環(huán)依賴的,循環(huán)依賴必須要持有引用。
循環(huán)依賴
循環(huán)依賴根據(jù)注入的時(shí)機(jī)分成兩種類型:
構(gòu)造器循環(huán)依賴。依賴的對(duì)象是通過(guò)構(gòu)造器傳入的,發(fā)生在實(shí)例化 Bean 的時(shí)候。
設(shè)值循環(huán)依賴。依賴的對(duì)象是通過(guò) setter 方法傳入的,對(duì)象已經(jīng)實(shí)例化,發(fā)生屬性填充和依賴注入的時(shí)候。
如果是構(gòu)造器循環(huán)依賴,本質(zhì)上是無(wú)法解決的。比如我們準(zhǔn)調(diào)用 A 的構(gòu)造器,發(fā)現(xiàn)依賴 B,于是去調(diào)用 B 的構(gòu)造器進(jìn)行實(shí)例化,發(fā)現(xiàn)又依賴 C,于是調(diào)用 C 的構(gòu)造器去初始化,結(jié)果依賴 A,整個(gè)形成一個(gè)死結(jié),導(dǎo)致 A 無(wú)法創(chuàng)建。
如果是設(shè)值循環(huán)依賴,Spring 框架只支持單例下的設(shè)值循環(huán)依賴。Spring 通過(guò)對(duì)還在創(chuàng)建過(guò)程中的單例,緩存并提前暴露該單例,使得其他實(shí)例可以引用該依賴。
3.3.1. 原型模式的循環(huán)依賴
Spring 不支持原型模式的任何循環(huán)依賴。檢測(cè)到循環(huán)依賴會(huì)直接拋出 BeanCurrentlyInCreationException 異常。
使用了一個(gè) ThreadLocal 變量 prototypesCurrentlyInCreation 來(lái)記錄當(dāng)前線程正在創(chuàng)建中的 Bean 對(duì)象,見 AbtractBeanFactory#prototypesCurrentlyInCreation:
/** Names of beans that are currently in creation */
private final ThreadLocal prototypesCurrentlyInCreation =
new NamedThreadLocal(“Prototype beans currently in creation”);
在 Bean 創(chuàng)建前進(jìn)行記錄,在 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)存空間,在單個(gè)元素時(shí) prototypesCurrentlyInCreation 只記錄 String 對(duì)象,在多個(gè)依賴元素后改用 Set 集合。這里是 Spring 使用的一個(gè)節(jié)約內(nèi)存的小技巧。
了解了記錄的寫入和刪除過(guò)程好了,再來(lái)看看讀取以及判斷循環(huán)的方式。這里要分兩種情況討論。
構(gòu)造函數(shù)循環(huán)依賴。
設(shè)置循環(huán)依賴。
這兩個(gè)地方的實(shí)現(xiàn)略有不同。
如果是構(gòu)造函數(shù)依賴的,比如 A 的構(gòu)造函數(shù)依賴了 B,會(huì)有這樣的情況。實(shí)例化 A 的階段中,匹配到要使用的構(gòu)造函數(shù),發(fā)現(xiàn)構(gòu)造函數(shù)有參數(shù) B,會(huì)使用 BeanDefinitionValueResolver 來(lái)檢索 B 的實(shí)例。見 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 {
@Autowired
private B b;
…
}
加載過(guò)程中,找到無(wú)參數(shù)構(gòu)造函數(shù),不需要檢索構(gòu)造參數(shù)的引用,實(shí)例化成功。接著執(zhí)行下去,進(jìn)入到屬性填充階段 AbtractBeanFactory.populateBean ,在這里會(huì)進(jìn)行 B 的依賴注入。
為了能夠獲取到 B 的實(shí)例化后的引用,最終會(huì)通過(guò)檢索類 DependencyDescriptor 中去把依賴讀取出來(lái),見 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á)成了同一。無(wú)論是構(gòu)造函數(shù)的循環(huán)依賴還是設(shè)置循環(huán)依賴,在需要注入依賴的對(duì)象時(shí),會(huì)繼續(xù)調(diào)用 beanFactory.getBean 去加載對(duì)象,形成一個(gè)遞歸操作。
而每次調(diào)用 beanFactory.getBean 進(jìn)行實(shí)例化前后,都使用了 prototypesCurrentlyInCreation 這個(gè)變量做記錄。按照這里的思路走,整體效果等同于 建立依賴對(duì)象的構(gòu)造鏈。
prototypesCurrentlyInCreation 中的值的變化如下:
原型模式的循環(huán)依賴
調(diào)用判定的地方在 AbstractBeanFactory.doGetBean 中,所有對(duì)象的實(shí)例化均會(huì)從這里啟動(dòng)。
// Fail if we’re already creating this bean instance:
// We’re assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
判定的實(shí)現(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ì)上使用同一種方式檢測(cè)出來(lái)。Spring 無(wú)法解決,直接拋出 BeanCurrentlyInCreationException 異常。
3.3.2. 單例模式的構(gòu)造循環(huán)依賴
Spring 也不支持單例模式的構(gòu)造循環(huán)依賴。檢測(cè)到構(gòu)造循環(huán)依賴也會(huì)拋出 BeanCurrentlyInCreationException 異常。
和原型模式相似,單例模式也用了一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)記錄正在創(chuàng)建中的 beanName。見 DefaultSingletonBeanRegistry:
/** Names of beans that are currently in creation */
private final Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
會(huì)在創(chuàng)建前進(jìn)行記錄,創(chuàng)建化后刪除記錄。
見 DefaultSingletonBeanRegistry.getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
…
// 記錄正在加載中的 beanName
beforeSingletonCreation(beanName);
...
// 通過(guò) singletonFactory 創(chuàng)建 bean
singletonObject = singletonFactory.getObject();
...
// 刪除正在加載中的 beanName
afterSingletonCreation(beanName);
}
記錄和判定的方式見 DefaultSingletonBeanRegistry.beforeSingletonCreation :
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
這里會(huì)嘗試往 singletonsCurrentlyInCreation 記錄當(dāng)前實(shí)例化的 bean。我們知道 singletonsCurrentlyInCreation 的數(shù)據(jù)結(jié)構(gòu)是 Set,是不允許重復(fù)元素的,所以一旦前面記錄了,這里的 add 操作將會(huì)返回失敗。
比如加載 A 的單例,和原型模式類似,單例模式也會(huì)調(diào)用匹配到要使用的構(gòu)造函數(shù),發(fā)現(xiàn)構(gòu)造函數(shù)有參數(shù) B,然后使用 BeanDefinitionValueResolver 來(lái)檢索 B 的實(shí)例,根據(jù)上面的分析,繼續(xù)調(diào)用 beanFactory.getBean 方法。
所以拿 A,B,C 的例子來(lái)舉例 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)存在了,檢測(cè)到構(gòu)造循環(huán)依賴,直接拋出異常結(jié)束操作。
3.3.3. 單例模式的設(shè)值循環(huán)依賴
單例模式下,構(gòu)造函數(shù)的循環(huán)依賴無(wú)法解決,但設(shè)值循環(huán)依賴是可以解決的。
這里有一個(gè)重要的設(shè)計(jì):提前暴露創(chuàng)建中的單例。
我們理解一下為什么要這么做。
還是拿上面的 A、B、C 的的設(shè)值依賴做分析,
=> 1. A 創(chuàng)建 -> A 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 B,啟動(dòng) B 的實(shí)例化
=> 2. B 創(chuàng)建 -> B 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 C,啟動(dòng) C 的實(shí)例化
=> 3. C 創(chuàng)建 -> C 構(gòu)造完成,開始注入屬性,發(fā)現(xiàn)依賴 A
重點(diǎn)來(lái)了,在我們的階段 1中, A 已經(jīng)構(gòu)造完成,Bean 對(duì)象在堆中也分配好內(nèi)存了,即使后續(xù)往 A 中填充屬性(比如填充依賴的 B 對(duì)象),也不會(huì)修改到 A 的引用地址。
所以,這個(gè)時(shí)候是否可以 提前拿 A 實(shí)例的引用來(lái)先注入到 C ,去完成 C 的實(shí)例化,于是流程變成這樣。
=> 3. C 創(chuàng)建 -> C 構(gòu)造完成,開始注入依賴,發(fā)現(xiàn)依賴 A,發(fā)現(xiàn) A 已經(jīng)構(gòu)造完成,直接引用,完成 C 的實(shí)例化。
=> 4. C 完成實(shí)例化后,B 注入 C 也完成實(shí)例化,A 注入 B 也完成實(shí)例化。
這就是 Spring 解決單例模式設(shè)值循環(huán)依賴應(yīng)用的技巧。流程圖為:
單例模式創(chuàng)建流程
為了能夠?qū)崿F(xiàn)單例的提前暴露。Spring 使用了三級(jí)緩存,見 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
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
這三個(gè)緩存的區(qū)別如下:
singletonObjects,單例緩存,存儲(chǔ)已經(jīng)實(shí)例化完成的單例。
singletonFactories,生產(chǎn)單例的工廠的緩存,存儲(chǔ)工廠。
earlySingletonObjects,提前暴露的單例緩存,這時(shí)候的單例剛剛創(chuàng)建完,但還會(huì)注入依賴。
從 getBean(“a”) 開始,添加的 SingletonFactory 具體實(shí)現(xiàn)如下:
protected Object doCreateBean … {
...
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
...
}
可以看到如果使用該 SingletonFactory 獲取實(shí)例,使用的是 getEarlyBeanReference 方法,返回一個(gè)未初始化的引用。
讀取緩存的地方見 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 獲取到引用后,存儲(chǔ)到 earlySingletonObjects 中。
這個(gè) earlySingletonObjects 的好處是,如果此時(shí)又有其他地方嘗試獲取未初始化的單例,可以從 earlySingletonObjects 直接取出而不需要再調(diào)用 getEarlyBeanReference。
從流程圖上看,實(shí)際上注入 C 的 A 實(shí)例,還在填充屬性階段,并沒有完全地初始化。等遞歸回溯回去,A 順利拿到依賴 B,才會(huì)真實(shí)地完成 A 的加載。
3.4. 創(chuàng)建實(shí)例
獲取到完整的 RootBeanDefintion 后,就可以拿這份定義信息來(lái)實(shí)例具體的 Bean。
具體實(shí)例創(chuàng)建見 AbstractAutowireCapableBeanFactory.createBeanInstance ,返回 Bean 的包裝類 BeanWrapper,一共有三種策略:
使用工廠方法創(chuàng)建,instantiateUsingFactoryMethod 。
使用有參構(gòu)造函數(shù)創(chuàng)建,autowireConstructor。
使用無(wú)參構(gòu)造函數(shù)創(chuàng)建,instantiateBean。
使用工廠方法創(chuàng)建,會(huì)先使用 getBean 獲取工廠類,然后通過(guò)參數(shù)找到匹配的工廠方法,調(diào)用實(shí)例化方法實(shí)現(xiàn)實(shí)例化,具體見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)建,整個(gè)過(guò)程比較復(fù)雜,涉及到參數(shù)和構(gòu)造器的匹配。為了找到匹配的構(gòu)造器,Spring 花了大量的工作,見 ConstructorResolver.autowireConstructor :
public BeanWrapper autowireConstructor … {
…
Constructor<?> constructorToUse = null;
…
// 匹配構(gòu)造函數(shù)的過(guò)程
…
beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(…);
…
bw.setBeanInstance(beanInstance);
return bw;
}
使用無(wú)參構(gòu)造函數(shù)創(chuàng)建是最簡(jiǎn)單的方式,見 AbstractAutowireCapableBeanFactory.instantiateBean:
protected BeanWrapper instantiateBean … {
…
beanInstance = getInstantiationStrategy().instantiate(…);
…
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
…
}
我們發(fā)現(xiàn)這三個(gè)實(shí)例化方式,最后都會(huì)走 getInstantiationStrategy().instantiate(…),見實(shí)現(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ù),并沒有立即實(shí)例化。因?yàn)橛脩羰褂昧?replace 和 lookup 的配置方法,用到了動(dòng)態(tài)代理加入對(duì)應(yīng)的邏輯。如果沒有的話,直接使用反射來(lái)創(chuàng)建實(shí)例。
創(chuàng)建實(shí)例后,就可以開始注入屬性和初始化等操作。
但這里的 Bean 還不是最終的 Bean。返回給調(diào)用方使用時(shí),如果是 FactoryBean 的話需要使用 getObject 方法來(lái)創(chuàng)建實(shí)例。見 AbstractBeanFactory.getObjectFromBeanInstance ,會(huì)執(zhí)行到 doGetObjectFromFactoryBean :
private Object doGetObjectFromFactoryBean … {
…
object = factory.getObject();
…
return object;
}
3.5. 注入屬性
實(shí)例創(chuàng)建完后開始進(jìn)行屬性的注入,如果涉及到外部依賴的實(shí)例,會(huì)自動(dòng)檢索并關(guān)聯(lián)到該當(dāng)前實(shí)例。
Ioc 思想體現(xiàn)出來(lái)了。正是有了這一步操作,Spring 降低了各個(gè)類之間的耦合。
屬性填充的入口方法在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 處理器,在屬性注入前后進(jìn)行處理。假設(shè)我們使用了 @Autowire 注解,這里會(huì)調(diào)用到 AutowiredAnnotationBeanPostProcessor 來(lái)對(duì)依賴的實(shí)例進(jìn)行檢索和注入的,它是 InstantiationAwareBeanPostProcessor 的子類。
根據(jù)名稱或者類型進(jìn)行自動(dòng)注入,存儲(chǔ)結(jié)果到 PropertyValues 中。
應(yīng)用 PropertyValues,填充到 BeanWrapper。這里在檢索依賴實(shí)例的引用的時(shí)候,會(huì)遞歸調(diào)用 BeanFactory.getBean 來(lái)獲得。
3.6. 初始化
3.6.1. 觸發(fā) Aware
如果我們的 Bean 需要容器的一些資源該怎么辦?比如需要獲取到 BeanFactory、ApplicationContext 等等。
Spring 提供了 Aware 系列接口來(lái)解決這個(gè)問(wèn)題。比如有這樣的 Aware:
BeanFactoryAware,用來(lái)獲取 BeanFactory。
ApplicationContextAware,用來(lái)獲取 ApplicationContext。
ResourceLoaderAware,用來(lái)獲取 ResourceLoaderAware。
ServletContextAware,用來(lái)獲取 ServletContext。
Spring 在初始化階段,如果判斷 Bean 實(shí)現(xiàn)了這幾個(gè)接口之一,就會(huì)往 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(jìn)行一些增強(qiáng)操作怎么辦?
這些增強(qiáng)操作比如打日志、做校驗(yàn)、屬性修改、耗時(shí)檢測(cè)等等。Spring 框架提供了 BeanPostProcessor 來(lái)達(dá)成這個(gè)目標(biāo)。比如我們使用注解 @Autowire 來(lái)聲明依賴,就是使用 AutowiredAnnotationBeanPostProcessor 來(lái)實(shí)現(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;
}
實(shí)現(xiàn)該接口的 Bean 都會(huì)被 Spring 注冊(cè)到 beanPostProcessors 中,見 AbstractBeanFactory :
/** BeanPostProcessors to apply in createBean */
private final List beanPostProcessors = new ArrayList();
只要 Bean 實(shí)現(xiàn)了 BeanPostProcessor 接口,加載的時(shí)候會(huì)被 Spring 自動(dòng)識(shí)別這些 Bean,自動(dòng)注冊(cè),非常方便。
然后在 Bean 實(shí)例化前后,Spring 會(huì)去調(diào)用我們已經(jīng)注冊(cè)的 beanPostProcessors 把處理器都執(zhí)行一遍。
public abstract class AbstractAutowireCapableBeanFactory … {
...
@Override
public Object applyBeanPostProcessorsBeforeInitialization ... {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization ... {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
...
}
這里使用了責(zé)任鏈模式,Bean 會(huì)在處理器鏈中進(jìn)行傳遞和處理。當(dāng)我們調(diào)用 BeanFactory.getBean 的后,執(zhí)行到 Bean 的初始化方法 AbstractAutowireCapableBeanFactory.initializeBean 會(huì)啟動(dòng)這些處理器。
protected Object initializeBean … {
…
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
…
// 觸發(fā)自定義 init 方法
invokeInitMethods(beanName, wrappedBean, mbd);
…
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
…
}
3.6.3. 觸發(fā)自定義 init
自定義初始化有兩種方式可以選擇:
實(shí)現(xiàn) InitializingBean。提供了一個(gè)很好的機(jī)會(huì),在屬性設(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)用者之前,還留有一個(gè)機(jī)會(huì)對(duì) Bean 實(shí)例進(jìn)行類型的轉(zhuǎn)換。見 AbstractBeanFactory.doGetBean :
protected T doGetBean … {
…
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
…
return getTypeConverter().convertIfNecessary(bean, requiredType);
…
}
return (T) bean;
}
- 總結(jié)
拋開一些細(xì)節(jié)處理和擴(kuò)展功能,一個(gè) Bean 的創(chuàng)建過(guò)程無(wú)非是:
獲取完整定義 -> 實(shí)例化 -> 依賴注入 -> 初始化 -> 類型轉(zhuǎn)換。
作為一個(gè)完善的框架,Spring 需要考慮到各種可能性,還需要考慮到接入的擴(kuò)展性。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-486321.html
所以有了復(fù)雜的循環(huán)依賴的解決,復(fù)雜的有參數(shù)構(gòu)造器的匹配過(guò)程,有了 BeanPostProcessor 來(lái)對(duì)實(shí)例化或初始化的 Bean 進(jìn)行擴(kuò)展修改。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-486321.html
到了這里,關(guān)于Spring中Bean加載流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!