SpringBoot實現(xiàn)自動配置的示例代碼
什么是自動配置?
Spring Boot 的自動配置:當 Spring 容器啟動后,一些配置類、bean 對象等就自動存入 Ioc 容器中,而不再需要我們手動去聲明,從而簡化了程序開發(fā)過程,省去了繁瑣的配置操作
也就是說,Spring Boot 的自動配置,就是 SpinrgBoot 將依賴 jar 包中的配置類以及 Bean 加載到 Spring Ioc 容器中的過程
在本篇文章中,我們主要學習一下兩個方面:
1. Spring 如何將對象加載到 Spring Ioc 容器中
2. SpringBoot 是如何進行實現(xiàn)的
我們首先來看 Spring 是如何加載 Bean 的
Spring 加載 Bean
當我們在項目中引入第三方的包時,其實就是在該項目下引入第三方的代碼,我們通過在該項目下創(chuàng)建不同的目錄來模擬第三方代碼的引入:

當前項目目錄為 com.example.springautoconfig,模擬第三方代碼文件在 com.example.autoconfig 目錄下
第三方文件代碼:
@Component
public class AutoConfig {
public void test() {
System.out.println("test...");
}
}獲取 AutoConfig:
@SpringBootTest
class SpringAutoconfigApplicationTests {
@Autowired
private ApplicationContext context;
@Test
void contextLoads() {
AutoConfig bean = context.getBean(AutoConfig.class);
System.out.println(bean);
}
}運行結果:

此時顯示沒有 com.example.autoconfig.AutoConfig 這個類型的 bean
為什么會報錯呢?
Spring 使用 類注解(@Controller、@Service、@Repository、@Component、@Configuration) 和 @Bean 注解 幫助我們將 Bean 加載到 Spring Ioc 容器中時,有一個前提:這些注解需要和 SpringBoot 啟動類(@SpringBootApplication 標注的類)在同一個目錄下
而在上述項目中,啟動類所在目錄為 com.example.springautoconfig,而 AutoConfig 類位于 com.example.autoconfig 目錄下,因此,SpringBoot 并沒有掃描到
可是,當我們引入第三方的 jar 包時,第三方的 jar 代碼目錄也不在啟動類的目錄下,那么,如何讓 Spring 幫我們管理這些 Bean 的呢?
我們可以通過指定路徑或引入的文件告訴 Spring,讓 Spring 進行掃描
常見的實現(xiàn)方法有兩種:
1. @ComponentScan 組件掃描
2. @Import 導入
@ComponentScan
使用 @ComponentScan 注解,指定 Spring 掃描路徑:
@SpringBootApplication
@ComponentScan("com.example.autoconfig")
public class SpringAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutoconfigApplication.class, args);
}
}運行程序并觀察結果:

成功獲取到 AutoConfig bean
那么,Spring Boot 是否使用了這種方式呢?
顯然沒有。若 Spring Boot 采用這種方式,當我們引入大量的第三方依賴,如 MyBatis、jackson 等時,就需要在啟動類上配置不同依賴需要掃描的包,非常繁瑣
@Import
@Import 導入主要有以下幾種形式:
1. 導入類
2. 導入 ImportSelector 接口的實現(xiàn)類
導入類
@Import(AutoConfig.class)
@SpringBootApplication
public class SpringAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutoconfigApplication.class, args);
}
}運行程序,觀察結果:

成功獲取到 AutoConfig Bean
若在文件中有多個配置項:
@Component
public class AutoConfig2 {
public void test() {
System.out.println("test...");
}
}此時就需要導入多個類:
@Import({AutoConfig.class, AutoConfig2.class})
@SpringBootApplication
public class SpringAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutoconfigApplication.class, args);
}
}顯而易見,這種方式也比較繁瑣,因此,Spring Boot 也沒有采用
導入 ImportSelector 接口的實現(xiàn)類
實現(xiàn) ImportSelector 接口:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 返回需要導入的全限定類名
return new String[]{"com.example.autoconfig.AutoConfig", "com.example.autoconfig.AutoConfig2"};
}
}導入:
@Import(MyImportSelector.class)
@SpringBootApplication
public class SpringAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutoconfigApplication.class, args);
}
}運行程序,并觀察結果:

這種方式也可以導入第三方依賴提供的 Bean
但是,它們都有一個明顯的問題:使用者需要知道第三方依賴中有哪些 Bean 對象或配置類,若我們在導入過程中漏掉了一些 Bean,就可能會導致我們的項目出現(xiàn)問題
依賴中有哪些 Bean,使用時需要配置哪些 Bean,這些問題第三方依賴最為清楚,那么,能否由第三方依賴來做這些事情呢?
比較常見的方法是第三方依賴提供一個注解,而這個注解一般是以 @EnableXxx 開頭的注解,而注解中封裝的就是 @Import 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 指定要導入哪些類
@Import(MyImportSelector.class)
public @interface EnableAutoConfig {
}在啟動類上使用第三方提供的注解:
@EnableAutoConfig
@SpringBootApplication
public class SpringAutoconfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAutoconfigApplication.class, args);
}
}運行并觀察結果:

可以看到,這種方式也可以導入第三方依賴提供的 Bean,且這種方式不需要使用方知道第三方依賴中有哪些 Bean 對象或配置類,而在 Spring Boot 中也是使用的這種方式
SpringBoot 原理分析
SpringBoot 是如何進行實現(xiàn)的?讓我們從 SpringBoot 的啟動類開始看:
由 @SpringBootApplication 標注的類就是 SpringBoot 項目的啟動類:

這個類與普通類的唯一區(qū)別就是 @SpringBootApplication 注解,這個注解也是 SpringBoot 實現(xiàn)自動配置的核心

@SpringBootApplication 是一個組合注解,注解中包含了:
1. 元注解:
JDK 中提供了 4 個 標準的用來對注解類型進行注解的注解類,稱之為 meta-annotation(元注解)
分別為:
@Retention:描述注解保留的時間范圍
@Target:描述注解的使用范圍
@Documented:描述在使用 javadoc 工具為類生成幫助文檔時是否保留其注解信息
@Inherited:使被其修飾的注解具有繼承性(若某個類使用了 @Inherited,則其子類將自動具有該注解)
2. @SpringBootConfiguration:

標識當前類是一個配置類,里面其實就是 @Configuration,只是做了進一步的封裝
其中,@Indexed 注解是用來加速應用啟動的
3. @EnableAutoConfiguration(開啟自動配置)
是 spiring 自動配置的核心注解,我們在后續(xù)詳細理解
4. ComponentScan(包掃描)
可以通過 basePackageClasses 或 basePackages 來定義要掃描的特定包,若沒有定義特定的包,將從聲明該注解的類的包開始掃描,這也是 SpringBoot 項目聲明的注解類為什么必須在啟動類目錄下

也可以自定義過濾器,用于排查一些類、注解等
接下來,我們重點來看 @EnableAutoConfiguration(開啟自動配置)
@EnableAutoConfiguration

@EnableAutoConfiguration 中主要包含兩部分:
@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
我們先來看 @Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class)
使用 @Import 注解,導入了實現(xiàn) ImportSelector 接口的實現(xiàn)類:

在 selectImports() 方法中,調用了 getAutoConfigurationEntry() 方法,獲取可自動配置的配置類信息
我們繼續(xù)看 getAutoConfigurationEntry() 方法:

在 getAutoConfigurationEntry() 方法中,主要通過 getCandidateConfigurations(annotationMetadata, attributes) 方法 和 fireAutoConfigurationImportEvents(configurations, exclusions) 方法,獲取在配置文件中配置的所有自動配置類的集合
我們先看 getCandidateConfigurations(annotationMetadata, attributes) 方法:

在 getCandidateConfigurations(annotationMetadata, attributes) 方法中,使用 ImportCandidates 進行加載
那么,從哪里進行加載呢?
從 斷言 的錯誤信息中我們可以找到答案:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
也就是說, getCandidateConfigurations 方法會獲取所有基于META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中配置類的集合
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:

其中包含了很多第三方依賴的配置文件
我們以 redis 為例:

查看 RedisAutoConfiguration:

可以看到,在使用 redis 時常用的 RedisTemplate 和 StringRedisTemplate 就位于其中,在我們需要使用時直接使用 @Autowired 進行注入就可以了
但是,由于當前項目并沒有引入 redis 相關 jar 包,此時這個類并不能被加載
也就是說在加載自動配置類的時候,并不是將所有的配置全部加載進來,而是會先進行判斷,根據(jù) @ConditionalOnMissingBean 、@ConditionalOnSingleCandidate 等注解的判斷進行動態(tài)加載
即,在配置文件中使用 @Bean 聲明對象,spring 會自動調用配置類中使用 @Bean 標識的方法,并將對象存放到 Spring Ioc 中,但是,在加載自動配置類的時候,并不是將所有的配置全部加載進來,而是會通過 @Conditional 等注解的判斷進行動態(tài)加載(@Conditional 是 spring 底層注解,會根據(jù)不同的條件,進行條件判斷,若滿足指定條件,配置類中的配置才會生效)
我們繼續(xù)看 fireAutoConfigurationImportEvents 方法,找到是從哪里獲取配置類的:

可以看到,fireAutoConfigurationImportEvents 方法最終會從 META-INF/spring.factories 中獲取配置類的集合
我們來看 META-INF/spring.factories:

META-INF/spring.factories 文件是 Spring 內部提供的一個約定俗稱的加載方式,只需要在模塊的 META-INF/spring.factories 文件中進行配置,Spring 就會把相應的實現(xiàn)類注入到 Spring 容器中
例如,有一個自動配置類,希望 SpringBoot 在啟動時自動加載這個配置,我們就可以在 META-INF/spring.factories 文件按照指定格式進行配置,讓 Spring Boot 應用啟動時,讀取這個 spring.factories 文件,根據(jù)文件中指定的配置類來進行自動配置
spring 會加載所有 jar 包下的 META-INF/spring.factories 文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 和 META-INF/spring.factories 都在引入的起步依賴中:

我們繼續(xù)看 AutoConfigurationPackage
AutoConfigurationPackage

在這個注解中,主要導入了一個配置文件 AutoConfigurationPackages.Registrar.class
我們繼續(xù)看 Registrar :

Registrar 實現(xiàn)了 ImportBeanDefinitionRegistrar 接口,可以被 @Import 導入到 spring 容器中
new PackageImports(metadata).getPackageNames().toArray(new String[0]): 當前啟動類所在的包名
也就是說,@AutoConfigurationPackage 的作用是 將啟動類所在的包下面所有的組件都掃描注冊到 spring 容器中
最后,我們來總結一下 SpringBoot 自動配置的流程
SpringBoot 自動配置流程
SpringBoot 的自動配置的入口是 @SpringBootApplication 注解,在這個注解中,主要封裝了 3 個注解:
@SpringBootConfiguration:標識當前類是配置類
@ComponentScan(包掃描):可以通過 basePackageClasses 或 basePackages 定義要掃描的特定包,若沒有定義特定的包,將從聲明該注解的類的包開始掃描,也就是說,默認情況下掃描的是啟動類所在的當前包以及子包
@EnableAutoConfiguration(開啟自動配置):主要包含兩部分:
@Import(AutoConfigurationImportSelector.class) :讀取 META-INF/spring.factories 和 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中定義的配置類????
@AutoConfigurationPackage:將啟動類所在的包下所有組件都注入到 Spring 容器中
到此這篇關于SpringBoot實現(xiàn)自動配置的示例代碼的文章就介紹到這了,更多相關SpringBoot自動配置內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
idea已經(jīng)提交到遠程分支,但需要本地和遠程都回退到某一版本問題
這篇文章主要介紹了idea已經(jīng)提交到遠程分支,但需要本地和遠程都回退到某一版本問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
MyBatis與Hibernate等ORM框架的區(qū)別及說明
MyBatis和Hibernate是Java中流行的ORM框架,各有特點:MyBatis采用半自動SQL映射,提供細粒度的SQL控制,適合復雜查詢和性能優(yōu)化;Hibernate采用全自動對象關系映射,提供較高的開發(fā)效率,適合簡單的CRUD操作,選擇哪種框架應根據(jù)項目需求、團隊技術棧和個人偏好來決定2025-03-03

