詳解@ConfigurationProperties如何裝載到Spring容器中
問題描述
最近項目中遇到了一個Spring
中@ConfigurationProperties
注解的問題,如下:
1.定義了一個注解了@ConfigurationProperties
的User Bean
。
@ConfigurationProperties(prefix?=?"my.user") @Component @Data public?class?User?{ ????private?String?userName; }
2.通過@Autowired
使用User
Bean,沒有問題。
@RestController @RequestMapping("/config") @EnableConfigurationProperties(User.class) public?class?UserConfigController?{ ????@Autowired ????private?User?user; ????@GetMapping("/username1") ????public?String?username1()?{ ????????return?user.getUserName(); ????} }
3.但是,有個同事修改了下變量名為user1
,自信的以為沒有問題,就提交測試了,然后直接報錯了。
@RestController @RequestMapping("/config") @EnableConfigurationProperties(User.class) public?class?UserConfigController?{ ????@Autowired ????private?User?user1; ????@GetMapping("/username2") ????public?String?username2()?{ ????????return?user1.getUserName(); ????} }
報錯如下圖所示:
這是怎么一回事呢,修改個變量名都能報錯?
原因分析
根據(jù)報錯信息不難分析出來主要原因在于User
類在Spring
容器中兩個Bean
對象,bean name
分別是“user
”和“my.user-com.alvinlkk.bean.User
”。
使用@Autwired
裝配,實際上不只是根據(jù)類型裝配,如果匹配到同類型有多個Bean對象,默認會去找和變量名“user
”同名的Bean
,所以不會報錯。如果修改變量名改成user1
, 它就匹配到兩個Bean對象,然后用bean name=user1
無法找到合適的,自然就報錯了。
那么為什么會出現(xiàn)兩個Bean呢?
1.因為使用@Component
注解,創(chuàng)建了一個名稱為“user
”的Bean。
2.使用了@EnableConfigurationProperties
注解創(chuàng)建了名稱為my.user-com.alvinlkk.bean.User
的Bean。
最佳實踐
使用@ConfigurationProperties
注解的Bean的時候,建議通過使用@EnableConfigurationProperties
創(chuàng)建Bean。
源碼解析
刨根問底,我們繼續(xù)從Spring源碼層面深入了解下這個問題的產(chǎn)生的根源。Spring創(chuàng)建Bean的過程其實很簡單,大致分兩個步驟:
- 創(chuàng)建Bean的定義信息
BeanDefinition
,包含Bean的類型,名稱等信息,注冊到Bean定義工廠中。 - 根據(jù)Bean定義工廠中的Bean定義信息,創(chuàng)建出Bean實例。
上面的兩個過程中在通常在SpringBoot啟動的過程中就完成,SpringBoot啟動的時候,會調(diào)用容器的refresh()
, 其中在invokeBeanFactoryPostProcessors(beanFactory)
方法中創(chuàng)建并注冊BeanDefinition
, 在finishBeanFactoryInitialization()
方法中創(chuàng)建Bean實例對象。
創(chuàng)建注冊BeanDefinition
@Component
注解
被Compoent
注解的的類會被Spring中的ConfigurationClassPostProcessor
類處理,創(chuàng)建出對應(yīng)的BeanDefinition
,然后注冊到BeanDefinitionRegistry
中,具體流程如下圖所示。
被@Component
注解的類User會被掃描到,生成一個名字是user
的BeanDefinition
,然后注冊到BeanDefitionRegistry中,如下圖所示:
@EnableConfigurationProperties
注解
注解@EnableConfigurationProperties
源碼中import
了EnableConfigurationPropertiesRegistrar
類,那么它是在什么階段創(chuàng)建出BeanDefinition
呢?
最終配置了@EnableConfigurationProperties(User.class)
中被獲取,創(chuàng)建出name為my.user-com.alvinlkk.bean.User
的BeanDefinition
,如下圖所示。
而且@Component
的順序是優(yōu)先于@EnableConfigurationProperties
的。
創(chuàng)建Bean對象
現(xiàn)在BeanDefinition
Bean定義信息已經(jīng)有了,Spring就可以根據(jù)這些信息創(chuàng)建出Bean對象實例了,這一個過程是在finishBeanFactoryInitialization()
方法中進行的,我們這里重點關(guān)注下@Autowird
方法是如何進行裝配的。
AbstractApplicationContext#refresh()
: 初始化容器AbstractApplicationContext#finishBeanFactoryInitialization()
: 初始化Bean入口DefaultListableBeanFactory#preInstantiateSingletons()
:預(yù)先初始化單例BeanDefaultListableBeanFactory#getBean()
: 調(diào)用getBean()
創(chuàng)建Bean實例AbstractBeanFactory#doGetBean()
:getBean()
最終調(diào)用的方法AbstractAutowireCapableBeanFactory#createBean()
: 創(chuàng)建Bean實例入口DefaultListableBeanFactory#determineAutowireCandidate()
:選擇使用哪個候選的Bean
根據(jù)類型匹配到Bean有多個的情況,會調(diào)用determineAutowireCandidate()
方法進一步去根據(jù)name匹配bean。
總結(jié)
所以對于配置注解ConfigurationProperties
的類不要使用使用@Component
注解讓Spring管理,更推薦的做法是使用@EnableConfigurationProperties
注解進行裝載。
以上就是詳解@ConfigurationProperties如何裝載到Spring容器中的詳細內(nèi)容,更多關(guān)于ConfigurationProperties裝載到Spring容器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring注入在有常量的情況下使用@AllArgsConstructor操作
這篇文章主要介紹了spring注入在有常量的情況下使用@AllArgsConstructor操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09maven打生產(chǎn)環(huán)境可執(zhí)行包的實現(xiàn)
本文主要介紹了maven打生產(chǎn)環(huán)境可執(zhí)行包的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01十分簡單易懂的Java應(yīng)用程序性能調(diào)優(yōu)技巧分享
這篇文章主要介紹了十分簡單易懂的Java性能調(diào)優(yōu)技巧分享,具有一定參考價值,需要的朋友可以了解下。2017-11-11MyBatis3傳遞多個參數(shù)(Multiple Parameters)
這篇文章主要介紹了MyBatis3傳遞多個參數(shù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07