1. 程式人生 > >品Spring:bean工廠後處理器的呼叫規則

品Spring:bean工廠後處理器的呼叫規則

上一篇文章介紹了對@Configuration類的處理邏輯,這些邏輯都寫在ConfigurationClassPostProcessor類中。


這個類不僅是一個“bean工廠後處理器”,還是一個“bean定義註冊後處理器”。

這其實是兩個介面,它們都是來操作bean定義。所以非常重要。

換句話說,能操作bean定義的,也只有這兩個介面,你說重要不重要。

檢視下型別資訊,在整個Spring中確實只有這兩個介面,如下圖01:


雖然它們都是進行和bean定義相關的操作,但目的卻是明顯不同的。

bean定義註冊後處理器,就是用來向容器中註冊bean定義的,造成的結果就是bean定義的數目變多。


如下圖02:


它的介面方法執行的時機是,所有的“常規bean定義”都已註冊完畢,該方法允許新增進一步的bean定義註冊到容器中。

程式設計新說注:這裡的“常規bean定義”指的是,在容器refresh前就已經註冊好的bean定義。

bean工廠後處理器,就是用來修改容器中的bean定義的,造成的結果就是bean定義的數目不變。

如下圖03:


它的介面方法執行的時機是,所有的bean定義都已經註冊完畢,不可能再增多了,該方法允許去修改bean定義的一些屬性。

後處理器除了可以作為bean定義註冊到容器中之外,還可以自己new出例項來,手動新增到容器中(此時不註冊bean定義)。


在容器的抽象類AbstractApplicationContext中,如下圖0405:


接下來就開始在refresh方法中呼叫後處理器了,如下圖06:


繼續隨著方法走,如下圖07:


可見有一個代理類專門來負責呼叫後處理器方法,其中第二個引數就是我們手動新增的後處理器例項(一般情況下沒有人新增,所以為空)。


在程式中,很多時候“順序”都是一個非常重要的事情,相同的程式碼,執行順序不同,可能就是不同的結果或報錯。

在Spring中,對順序的處理是有統一的方案的,就是介面或註解。

首先是Ordered介面,如下圖08:


使用一個int型別的值表示順序,很簡單。


需要注意的是,優先順序最高的卻是負數最小值,優先順序最低的卻是正數最大值。即數值越小優先順序越高。

然後是PriorityOrdered介面,如下圖09:


它只是繼承了Ordered介面,啥也沒做。從名字就能看出來該介面的優先順序比Ordered介面要高。

接著是@Order註解,如下圖10:


從Spring4.1開始,標準的Java註解@javax.annotation.Priority,可以作為臨時替代使用,如下圖11:


在具體使用時,同一類元件最好保持風格統一,都使用介面,或都使用註解。

還有一種情況需要注意,就是對於既沒有實現介面也沒有標註解的類,會給它一個預設的順序值。

一般情況下是0或最大值或最小值。就是處在中間位置,或優先順序最低,或優先順序最高。


下面開始具體呼叫這些後處理器,有好幾個方面的順序問題:

1)先呼叫手動新增的後處理器,再呼叫作為bean定義註冊的後處理器

2)先呼叫bean定義註冊後處理器,再呼叫bean工廠後處理器

3)先呼叫註冊bean定義的介面方法,再呼叫修改bean定義的介面方法

4)先呼叫實現PriorityOrdered介面的,再呼叫實現Ordered介面的,最後是沒有實現介面的

整個呼叫過程分為很多步進行:

第一步,先呼叫手動新增的bean定義註冊後處理器的註冊bean定義方法,如下圖12:


第二步,再呼叫容器中實現PriorityOrdered介面的bean定義註冊後處理器的註冊bean定義方法,如下圖13:


第三步,再呼叫容器中實現Ordered介面的bean定義註冊後處理器的註冊bean定義方法,如下圖14:


第四步,再通過迴圈呼叫容器中剩餘所有的bean定義註冊後處理器的註冊bean定義方法,如下圖15:


程式設計新說注:

此處為什麼要通過迴圈一直呼叫呢?因為這是在註冊bean定義,而且註冊的bean定義可能又是一個bean定義註冊後處理器。

這很好理解,就像買飲料遇到再來一瓶一樣的道理。

你買了10瓶,全部開啟,有8個再來一瓶,老闆又給了你8瓶,再全部開啟,有5個再來一瓶,老闆再給你5瓶,你接著再開啟。

如此反覆,直到沒有遇到再來一瓶為止。

截止到目前,所有註冊bean定義的方法都已經調完,這意味著bean定義註冊已經完畢,bean定義的數目不會再增多了。

第五步,呼叫所有bean定義註冊後處理器的修改bean定義方法,按需對bean定義進行修改或完善,執行順序和上面保持一致,如下圖16:


截止到目前,所有的bean定義註冊後處理器介面已經全部呼叫完畢。接下來該呼叫bean工廠後處理器了。

第六步,呼叫手動新增的bean工廠後處理器的修改bean定義方法,如下圖17:


第七步,呼叫容器中實現PriorityOrdered介面的bean工廠後處理器的修改bean定義方法,如下圖18:


程式設計新說注:

可以看到這裡的寫法和上面不太一樣,上面每次都從容器中獲取,是因為bean定義的數量一直在增加。

現在bean定義數量不會再變了,從容器中獲取一次即可,一個迴圈就可以按實現的介面不同把它們分開。

第八步,呼叫容器中實現Ordered介面的bean工廠後處理器的修改bean定義方法,如下圖19:


第九步,呼叫容器中沒有實現介面的bean工廠後處理器的修改bean定義方法,如下圖20:


因為沒有實現介面,所以這一步不用排序。


截止到現在,所有的bean定義都已經修改完畢。bean定義的屬性不會再有任何變化了。

總結一下:

本文介紹了兩個“後處理器”介面,一個用於註冊bean定義,一個用於修改bean定義。

這也是Spring中僅有的兩個能夠操作bean定義的介面。所以它們非常重要。

然後它們的呼叫順序也很重要,如先註冊bean定義,才能修改bean定義。

還有對PriorityOrdered介面和Ordered介面,以及沒有介面的應用。

對bean定義註冊後處理器採用類似“再來一瓶”的呼叫方式。因為新增加的bean定義可能還是這種型別的。

對bean工廠後處理器採用普通的呼叫方式,因為bean定義數量不再變化。

截止到目前,所有的bean定義已經全部就緒,等待著進入下一個階段。

 

(END)

 

>>> 品Spring系列文章 <<<

 

品Spring:帝國的基石

品Spring:bean定義上梁山

品Spring:實現bean定義時採用的“先進生產力”

品Spring:註解終於“成功上位”

品Spring:能工巧匠們對註解的“加持”

品Spring:SpringBoot和Spring到底有沒有本質的不同?

品Spring:負責bean定義註冊的兩個“排頭兵”

品Spring:SpringBoot輕鬆取勝bean定義註冊的“第一階段”

品Spring:SpringBoot發起bean定義註冊的“二次攻堅戰”

品Spring:註解之王@Configuration和它的一眾“小弟們”

 

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

【面試】我是如何面試別人List相關知識的,深度有點長文

我是如何在畢業不久只用1年就升為開發組長的

爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

【面試】我是如何在面試別人Spring事務時“套路”對方的

【面試】Spring事務面試考點吐血整理(建議珍藏)

【面試】我是如何在面試別人Redis相關知識時“軟懟”他的

【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)

【面試】如果你這樣回答“什麼是執行緒安全”,面試官都會對你刮目相看(建議珍藏)

【面試】迄今為止把同步/非同步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)

【面試】一篇文章幫你徹底搞清楚“I/O多路複用”和“非同步I/O”的前世今生(深度好文,建議珍藏)

【面試】如果把執行緒當作一個人來對待,所有問題都瞬間明白了

Java多執行緒通關———基礎知識挑戰

品Spring:帝國的基石

 

作者是工作超過10