Spring Boot 的注解生效詳細(xì)步驟解析


在 Spring 中,@Configuration、@ComponentScan、@Bean、@Import 等注解的掃描、解析和 BeanDefinition 注冊(cè)是一個(gè)分層處理的過(guò)程。下面我們以 @Configuration 類為例,結(jié)合代碼流程詳細(xì)說(shuō)明其從掃描到注冊(cè)的完整邏輯。
1. 整體流程概覽
以下是核心步驟的流程圖:
1. 掃描候選配置類 → 2. 解析注解元數(shù)據(jù) → 3. 注冊(cè) BeanDefinition
具體分為以下階段:
- 掃描階段:通過(guò)
BeanDefinitionRegistry獲取所有候選配置類。 - 解析階段:使用
ConfigurationClassParser解析注解(如@ComponentScan、@Bean、@Import)。 - 注冊(cè)階段:通過(guò)
ConfigurationClassBeanDefinitionReader將解析結(jié)果注冊(cè)為BeanDefinition。
2. 詳細(xì)步驟解析
2.1 掃描階段:識(shí)別候選配置類
觸發(fā)入口:ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
邏輯:
- 從
BeanDefinitionRegistry獲取所有已注冊(cè)的BeanDefinition名稱:String[] beanNames = registry.getBeanDefinitionNames();
- 遍歷這些名稱,檢查對(duì)應(yīng)的
BeanDefinition是否是候選配置類:- 條件:類上有
@Configuration、@Component、@ComponentScan、@Import、@ImportResource,或類中有@Bean方法。 - 判斷邏輯:
if (isFullConfigurationCandidate(beanDef) || isLiteConfigurationCandidate(beanDef)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } isFullConfigurationCandidate(beanDef):檢查是否有@Configuration注解。isLiteConfigurationCandidate(beanDef):檢查是否有其他相關(guān)注解(如@Component、@Bean方法)。
- 條件:類上有
關(guān)鍵點(diǎn):
- 掃描的輸入是已注冊(cè)的
BeanDefinition(可能來(lái)自 XML、Java Config 或自動(dòng)掃描)。 - 此時(shí)尚未解析注解內(nèi)容,僅識(shí)別出需要進(jìn)一步處理的候選類。
2.2 解析階段:處理注解元數(shù)據(jù)
核心類:ConfigurationClassParser
入口方法:parse()
邏輯:遞歸解析每個(gè)候選配置類的注解。
(1) 解析@ComponentScan
- 作用:掃描指定包路徑下的
@Component類(如@Service、@Repository)。 - 流程:
- 獲取
@ComponentScan注解的basePackages或basePackageClasses。 - 使用
ClassPathBeanDefinitionScanner掃描類路徑:scanner.scan(basePackages);
- 掃描到的類會(huì)被注冊(cè)為新的
BeanDefinition(類型為ScannedGenericBeanDefinition)。
- 獲取
- 關(guān)鍵點(diǎn):
- 掃描時(shí)使用
ASM或反射讀取類注解,避免提前加載類到 JVM。 - 新注冊(cè)的
BeanDefinition可能也會(huì)被后續(xù)解析(如果它們也是配置類)。
- 掃描時(shí)使用
(2) 解析@Bean方法
- 作用:將配置類中的
@Bean方法轉(zhuǎn)換為BeanDefinition。 - 流程:
- 遍歷配置類中的所有方法,篩選帶
@Bean注解的方法。 - 為每個(gè)
@Bean方法生成一個(gè)BeanDefinition:- 類型:
ConfigurationClassBeanDefinition。 - 工廠方法:設(shè)置為
@Bean方法(通過(guò)factoryMethodName和factoryBeanName指定)。 - 依賴:解析
@Bean方法的參數(shù)(按類型或@Qualifier注入)。
- 類型:
- 遍歷配置類中的所有方法,篩選帶
- 示例:
@Configuration public class AppConfig { @Bean public DataSource dataSource() { return new HikariDataSource(); } }- 生成的
BeanDefinition會(huì)記錄:factoryBeanName=appConfig,factoryMethodName=dataSource。
- 生成的
(3) 解析@Import
- 作用:動(dòng)態(tài)導(dǎo)入其他配置類或
BeanDefinition。 - 三種處理方式:
- 普通類:直接注冊(cè)為
BeanDefinition。@Import(OtherConfig.class)
ImportSelector:通過(guò)編程方式選擇要導(dǎo)入的類。@Import(MyImportSelector.class)
MyImportSelector實(shí)現(xiàn)selectImports()方法,返回要導(dǎo)入的類名數(shù)組。
ImportBeanDefinitionRegistrar:直接注冊(cè)BeanDefinition。@Import(MyRegistrar.class)
MyRegistrar實(shí)現(xiàn)registerBeanDefinitions()方法,手動(dòng)操作BeanDefinitionRegistry。
- 普通類:直接注冊(cè)為
(4) 處理父類與接口
- 遞歸檢查配置類的父類和接口,確保不遺漏任何
@Bean方法或元注解。
2.3 注冊(cè)階段:加載BeanDefinition
核心類:ConfigurationClassBeanDefinitionReader
入口方法:loadBeanDefinitions()
邏輯:將解析結(jié)果(ConfigurationClass 對(duì)象)轉(zhuǎn)換為 BeanDefinition 并注冊(cè)到容器。
(1) 注冊(cè)@Import的類
- 普通類:通過(guò)
registry.registerBeanDefinition()直接注冊(cè)。 ImportBeanDefinitionRegistrar:調(diào)用其registerBeanDefinitions()方法。
(2) 注冊(cè)@Bean方法
- 為每個(gè)
@Bean方法生成BeanDefinition并注冊(cè):for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); }
(3) 處理嵌套配置類
- 如果配置類內(nèi)部有
@Configuration靜態(tài)嵌套類,遞歸處理。
3. 關(guān)鍵設(shè)計(jì)點(diǎn)
(1) 延遲加載與遞歸處理
- 延遲加載:
@ComponentScan掃描到的類可能也是配置類,需要遞歸解析。 - 循環(huán)依賴處理:Spring 通過(guò)提前暴露
BeanDefinition解決配置類之間的循環(huán)引用。
(2) 元數(shù)據(jù)存儲(chǔ)
ConfigurationClass對(duì)象存儲(chǔ)解析后的中間結(jié)果(如@Bean方法、@Import類等)。BeanDefinition的attribute字段存儲(chǔ)配置類的元信息(如@Lazy、@Primary)。
(3) 性能優(yōu)化
- ASM 字節(jié)碼分析:在掃描階段避免加載類到 JVM。
- 緩存:解析結(jié)果緩存到
ConfigurationClass中,避免重復(fù)處理。
4. 示例全流程
場(chǎng)景
@Configuration
@ComponentScan("com.example.service")
@Import(OtherConfig.class)
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}步驟
- 掃描階段:
- 發(fā)現(xiàn)
AppConfig是候選配置類(有@Configuration)。
- 發(fā)現(xiàn)
- 解析階段:
- 解析
@ComponentScan:掃描com.example.service包,注冊(cè)@Service類。 - 解析
@Import(OtherConfig.class):遞歸處理OtherConfig。 - 解析
@Bean dataSource():生成工廠方法BeanDefinition。
- 解析
- 注冊(cè)階段:
- 注冊(cè)
OtherConfig及其@Bean方法。 - 注冊(cè)
dataSource的BeanDefinition。
- 注冊(cè)
5. 總結(jié)
Spring 對(duì)配置類注解的處理是一個(gè)分層遞歸的過(guò)程:
- 掃描:通過(guò)
BeanDefinitionRegistry篩選候選類。 - 解析:
ConfigurationClassParser解析注解并生成中間結(jié)果(ConfigurationClass)。 - 注冊(cè):
ConfigurationClassBeanDefinitionReader將解析結(jié)果轉(zhuǎn)換為BeanDefinition。
這種設(shè)計(jì)將注解元數(shù)據(jù)解析與 BeanDefinition 注冊(cè)分離,確保了靈活性和擴(kuò)展性(如支持動(dòng)態(tài) ImportSelector)。同時(shí),遞歸處理和緩存機(jī)制解決了復(fù)雜依賴和性能問(wèn)題。
到此這篇關(guān)于Spring Boot 的注解生效詳細(xì)步驟解析的文章就介紹到這了,更多相關(guān)Spring Boot 注解生效內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java ArrayList 數(shù)組之間相互轉(zhuǎn)換
本文通過(guò)代碼示例給大家講解arraylist轉(zhuǎn)化為數(shù)組,然后數(shù)組轉(zhuǎn)化為arraylist的相關(guān)資料,感興趣的朋友一起看看吧2015-11-11
Springboot使用Spring Data JPA實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 規(guī)范的基礎(chǔ)上開(kāi)發(fā)的一個(gè)框架,使用 Spring Data JPA 可以極大地簡(jiǎn)化JPA 的寫法,本章我們將詳細(xì)介紹在Springboot中使用 Spring Data JPA 來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的操作2021-06-06
詳解在Spring MVC或Spring Boot中使用Filter打印請(qǐng)求參數(shù)問(wèn)題
這篇文章主要介紹了詳解在Spring MVC或Spring Boot中使用Filter打印請(qǐng)求參數(shù)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
6種Java創(chuàng)建對(duì)象的方式總結(jié)
在Java中,創(chuàng)建對(duì)象可以使用多種方式,本文將詳細(xì)介紹以下六種創(chuàng)建對(duì)象的方式,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04
Java數(shù)據(jù)結(jié)構(gòu)之散列表詳解
散列表(Hash table,也叫哈希表),是根據(jù)關(guān)鍵碼值(Key value)而直接進(jìn)行訪問(wèn)的數(shù)據(jù)結(jié)構(gòu)。本文將為大家具體介紹一下散列表的原理及其代碼實(shí)現(xiàn)2022-01-01
Java?Web項(xiàng)目中如何添加Tomcat的Servlet-api.jar包(基于IDEA)
servlet-api.jar是在編寫servlet必須用到的jar包下面這篇文章主要給大家介紹了基于IDEAJava?Web項(xiàng)目中如何添加Tomcat的Servlet-api.jar包的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-04-04

