答讀者問:BeanFactoryPostProcessor 似乎失效了?
有小伙伴在學習 Spring 源碼視頻的時候,看了松哥講的 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 自動解析的。
好啦,現在小伙伴提出的問題大家伙都明白了吧?