品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年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號和知識星球的二維碼,歡迎關注!

 

       

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM