答讀者問:BeanFactoryPostProcessor 似乎失效了?

帶你聊技術發表於2023-11-08

來源:江南一點雨


有小夥伴在學習 Spring 原始碼影片的時候,看了松哥講的 BeanFactoryPostProcessor 的用法之後,提出了這樣一個問題:

答讀者問:BeanFactoryPostProcessor 似乎失效了?答讀者問:BeanFactoryPostProcessor 似乎失效了?

我來跟大家補充一下這個問題的上下文:

我講了 BeanFactoryPostProcessor,分析了其原理,也講了具體的使用場景,一個典型的使用場景是我們在 XML 中定義 Bean 的時候,如果 Bean 的屬性是使用了 properties 檔案佔位符如 ${db.username} 這種,那麼在 BeanFactoryPostProcessor 階段,就會對這個佔位符進行處理,將其替換成真正的 value。然後我還順手給大家舉了一個例子,我在 XML 檔案中定義 Bean 的時候,給 Bean 的某一個屬性設定 value 為 ^username,然後在 BeanFactoryPostProcessor 中,我將 ^username 改為某一個字串。

小夥伴看了松哥講的內容之後,也照著寫了一個,就是上面圖片中的程式碼,不同的是,他是將 XML 配置改為了 Java 程式碼配置,結果發現屬性 hok 並未變為 NB,因此有了上述問題。

我覺得這個問題問的很好,給了小夥伴們一個從其他方面理解 Spring 的機會,這也是我前面一直強調的,這次的 Spring 影片需要各位小夥伴一起發力,大家有關於 Spring 的任何問題都可以提,我負責透過原始碼來回答你。

問題分析

這個問題的分析,得先從 BeanDefinition 開始。在講 BeanFactoryPostProcessor 之前,松哥已經和小夥伴們分析過 BeanDefinition 了,無論我們是透過 Java 程式碼還是透過 XML 檔案定義的 Bean 物件,在解析稱為 Bean 物件之前,得先解析成為 BeanDefinition,BeanDefinition 則有不同的分類,對於 XML 檔案定義的 Bean,最終解析為 GenericBeanDefinition,而透過 @Bean 註解定義的 Bean 則解析為 ConfigurationClassBeanDefinition。

但是這兩個的處理原理顯然是有差異的。

對於 XML 定義的 Bean 來說,很明顯 XML 中的所有屬性都要先解析到 BeanDefinition 中,包括我們在 XML 中配置的 Bean 的各種屬性,這一步是在 Spring 容器 refresh 方法中構建 BeanFactory 的時候完成的(obtainFreshBeanFactory 方法),這一步完成之後,在後面的步驟會去執行容器中所有的 BeanFactoryPostProcessor(invokeBeanFactoryPostProcessors),此時就會把前面解析出來的 BeanDefinition 中帶有佔位符的屬性給替換過來,最後在 refresh 方法中執行 finishBeanFactoryInitialization 方法完成 Bean 的初始化。

按照上面這一套流程順序,佔位符被解析成為正常字串沒什麼問題。

但是,如果是 @Bean 註解配置的 Bean,則會有所差異。

首先,@Bean 註解所標記的方法要被解析為一個 ConfigurationClassBeanDefinition,這個過程本身是透過 ConfigurationClassPostProcessor 來完成的,而 ConfigurationClassPostProcessor 本質上其實就是一個 BeanFactoryPostProcessor,換言之,@Bean 註解標記的方法是在 BeanFactoryPostProcessor 中被解析為 ConfigurationClassBeanDefinition 的。ConfigurationClassBeanDefinition 這個 BeanDefinition 主要用來記錄 @Bean 註解所標記的方法所屬的物件、方法的名稱、方法物件、方法引數、註解的引數等等資訊,把這些資訊記錄下來,將來在初始化 Bean 的時候,透過反射執行目標方法就可以了,即方法裡邊的內容是什麼,ConfigurationClassBeanDefinition 其實並不關心。

最後則是和 XML 一樣,在 finishBeanFactoryInitialization 方法中完成 Bean 的初始化。

經過上面分析,小夥伴們可以看到,透過 @Bean 註解定義的 Bean,我們為屬性賦值是在方法內部完成的,這些方法內部的邏輯其實並未被解析到 BeanDefinition 中,顯然也沒有必要把方法內部的邏輯解析到 BeanDefinition 上去,因此,透過 @Bean 註解定義的 Bean,如果屬性中使用了佔位符,是無法透過 BeanFactoryPostProcessor 自動解析的。

好啦,現在小夥伴提出的問題大傢伙都明白了吧?

以上的分析中,方法的具體邏輯在 Spring 原始碼中都有詳細講解,所以這裡我只是和大家梳理了思路,具體實現小夥伴可以參考我們的原始碼影片。

歡迎各位小夥伴在學習過程中繼續提出高質量問題,一起把這套 Spring 原始碼教程做紮實了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024922/viewspace-2993493/,如需轉載,請註明出處,否則將追究法律責任。

相關文章