国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring中Bean加載流程

這篇具有很好參考價(jià)值的文章主要介紹了Spring中Bean加載流程。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

  1. 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的解析
Spring中Bean加載流程

  1. 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)品。
  1. 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)行自定義加工。

Spring中Bean加載流程

3.2 核心步驟
Spring中Bean加載流程

 - 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ì)流程:
Spring中Bean加載流程
上面是跟蹤了 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)分析。

  1. 細(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;
}

  1. 總結(jié)
    拋開一些細(xì)節(jié)處理和擴(kuò)展功能,一個(gè) Bean 的創(chuàng)建過(guò)程無(wú)非是:

獲取完整定義 -> 實(shí)例化 -> 依賴注入 -> 初始化 -> 類型轉(zhuǎn)換。

作為一個(gè)完善的框架,Spring 需要考慮到各種可能性,還需要考慮到接入的擴(kuò)展性。

所以有了復(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)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring IOC 之加載 BeanDefinition

    Spring IOC 之加載 BeanDefinition

    ??作者簡(jiǎn)介:大家好,我是小徐?? ??博客首頁(yè):CSDN主頁(yè) 小徐的博客 ??每日一句: 好學(xué)而不勤非真好學(xué)者 前面的文章我們已經(jīng)對(duì)IOC之Spring統(tǒng)一資源加載策略有了一定的了解,本文我們將探討Spring IOC 加載 BeanDefinition的整個(gè)過(guò)程。 我們先先看一段熟悉的代碼: 這段代碼是

    2024年01月17日
    瀏覽(23)
  • spring啟動(dòng)流程 (3) BeanDefinition詳解

    spring啟動(dòng)流程 (3) BeanDefinition詳解

    BeanDefinition在Spring初始化階段保存Bean的元數(shù)據(jù)信息,包括Class名稱、Scope、構(gòu)造方法參數(shù)、屬性值等信息,本文將介紹一下BeanDefinition接口、重要的實(shí)現(xiàn)類,以及在Spring中的使用示例。 用于描述了一個(gè)Bean實(shí)例,該Bean實(shí)例具有屬性、構(gòu)造方法參數(shù)以及由具體實(shí)現(xiàn)提供的其他信息

    2024年02月12日
    瀏覽(16)
  • Spring Boot 如何讓你的 bean 在其他 bean 之前完成加載 ?

    Spring Boot 如何讓你的 bean 在其他 bean 之前完成加載 ?

    今天有個(gè)小伙伴給我出了一個(gè)難題:在 SpringBoot 中如何讓自己的某個(gè)指定的 Bean 在其他 Bean 前完成被 Spring 加載?我聽到這個(gè)問(wèn)題的第一反應(yīng)是,為什么會(huì)有這樣奇怪的需求? Talk is cheap,show me the code,這里列出了那個(gè)想做最先加載的“天選 Bean” 的代碼,我們來(lái)分析一下:

    2024年02月03日
    瀏覽(18)
  • Spring Boot |如何讓你的 bean 在其他 bean 之前完成加載

    Spring Boot |如何讓你的 bean 在其他 bean 之前完成加載

    問(wèn)題 今天有個(gè)小伙伴給我出了一個(gè)難題:在 SpringBoot 中如何讓自己的某個(gè)指定的 Bean 在其他 Bean 前完成被 Spring 加載?我聽到這個(gè)問(wèn)題的第一反應(yīng)是,為什么會(huì)有這樣奇怪的需求? Talk is cheap,show me the code,這里列出了那個(gè)想做最先加載的“天選 Bean” 的代碼,我們來(lái)分析一

    2024年02月05日
    瀏覽(19)
  • Spring bean定義&Spring Bean 的作用域

    目錄 Spring bean定義 ? Spring配置元數(shù)據(jù) Spring Bean 的作用域 ? singleton作用域: ? 原型作用域: 示例: ? 形成應(yīng)用程序的骨干是由Spring IoC容器所管理的對(duì)象稱為bean。bean被實(shí)例化,組裝,并通過(guò)Spring IoC容器所管理的對(duì)象。這些bean由容器提供,例如,在XML的bean/定義,已經(jīng)看到了

    2024年02月08日
    瀏覽(25)
  • 面試篇-揭開Spring Bean加載的神秘面紗

    面試篇-揭開Spring Bean加載的神秘面紗

    ? 啟動(dòng)spring容器(創(chuàng)建beanfactory)-加載配置(注解、xml)-實(shí)例化bean(執(zhí)行構(gòu)造方法)-注入依賴-初始化bean(設(shè)置屬性值)-使用-銷毀 解析和讀取 XML 配置文件或注解配置類,獲取 Bean 定義信息。 根據(jù) Bean 定義信息實(shí)例化 Bean 對(duì)象。根據(jù)不同的作用域(如 singleton、prototype 等),S

    2023年04月17日
    瀏覽(30)
  • 【微服務(wù)】spring 控制bean加載順序使用詳解

    目錄 一、前言 二、使用@order注解控制順序 2.1 @order 注解使用示例 2.2 order注解順序失效問(wèn)題

    2024年02月08日
    瀏覽(18)
  • spring啟動(dòng)流程 (2) Bean實(shí)例化流程

    本文通過(guò)閱讀Spring源碼,分析Bean實(shí)例化流程。 上一篇文章已經(jīng)介紹,Bean實(shí)例化入口在AbstractApplicationContext類的finishBeanFactoryInitialization方法: 返回指定beanName的(原始)單例對(duì)象,如果沒有則創(chuàng)建一個(gè)新對(duì)象: 創(chuàng)建Bean實(shí)例、填充屬性、調(diào)用后置處理器等:

    2024年02月11日
    瀏覽(29)
  • Spring的加載配置文件、容器和獲取bean的方式

    Spring的加載配置文件、容器和獲取bean的方式

    ??個(gè)人主頁(yè): ?? 葉落閑庭 ??我的專欄:?? c語(yǔ)言 數(shù)據(jù)結(jié)構(gòu) javaweb 石可破也,而不可奪堅(jiān);丹可磨也,而不可奪赤。 properties文件: jdbc.properties 1.開啟context命名空間 2.使用context命名空間,加載指定properties文件 3.使用屬性占位符 ${} 讀取properties文件中的屬性 properties文件

    2024年02月15日
    瀏覽(22)
  • 超越競(jìng)爭(zhēng):Spring Boot如何在加載bean時(shí)優(yōu)先選擇我?

    超越競(jìng)爭(zhēng):Spring Boot如何在加載bean時(shí)優(yōu)先選擇我?

    ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! Spring Boot 是當(dāng)前業(yè)界最受歡迎和廣泛使用的 Java Web 應(yīng)用開發(fā)框架之一。在 Spring Boot 應(yīng)用中,bean 是通過(guò)自動(dòng)配置進(jìn)行裝載的,因?yàn)槠浒凑占s定順序位置,Spring Boot 希望盡可能提供正確的自動(dòng)配置,在應(yīng)用運(yùn)行時(shí)重寫或自

    2023年04月08日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包