前言
前面第一章的內容基本上都是基於註解的Spring內容,想要學好SpringBoot這些Spring基礎必不可少。從這章開始就是SpringBoot了,首先學習一下SpringBoot最重要的自動裝配原理。
一句話概括SpringBoot的自動配置原理就是:啟動時載入所有,最終按照條件進行裝配。
本小節重點註解結構列圖如下:
@SpringBootApplication:主程式註解;
@SpringBootConfiguration
:表名核心配置類;- @Configuration:表名配置類;
@ComponentScan
:開啟包掃描;@EnableAutoConfiguration
:啟用自動配置;- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
- @Import(AutoConfigurationPackages.Registrar.class):容器中匯入Registrar元件;
- @Import(AutoConfigurationImportSelector.class):【重要】按照條件配裝規則@Conditional,按需配置;
- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
注:在說明註解時,第一點加粗為註解中文含義,第二點為一般加在哪身上,縮排或程式碼塊為示例,如:
@註解
- 中文含義
- 加在哪
- 其他……
語句示例
//程式碼示例
1. 引入配置檔案與配置繫結
在理解自動裝配原理之前,還需要知道配置繫結相關注解。
引入配置檔案常用@ImportResource
註解:
@ImportResource
- 配置引入;
- 用於類上;
- 用於引入配置檔案,常用於老專案(需要保留大量xml檔案的專案);
@ImportResource("classpath:beans.xml")
引入beans.xml配置檔案。
配置繫結的核心註解為@ConfigurationProperties
,指讀取properties檔案中的內容,封裝進JavaBean中,以供隨時使用。
@ConfigurationProperties
- 屬性配置;
- 用於POJO類上;
配置繫結有兩種形式:
1.1 @ConfigurationProperties + @EnableConfigurationProperties
-
EnableConfigurationProperties:啟用配置屬性;
-
在POJO類上寫
@ConfigurationProperties
,在配置類上寫@EnableConfigurationProperties
; -
@EnableConfigurationProperties
的兩個核心功能:將按照制定規則與配置檔案繫結、將元件匯入容器中;- 例:將Car元件自動註冊進容器中:
//配置類 @EnableConfigurationProperties(Car.class) public class MyConfig { } //POJO類 @ConfigurationProperties(prefix = "mycar") public class Car { private String brand; private Integer price; }
#配置檔案 mycar: brand: 小鵬 price: 100000
1.2 @ConfigurationProperties + @Component
-
Component:元件注入;
-
在POJO類上同時標註這兩個註解;
-
@Component
表示將POJO類作為元件註冊進容器中,只有在容器中的元件, 才會擁有SpringBoot提供的強大功能;- 例:將Car元件自動註冊進容器中:
@Component @ConfigurationProperties(prefix = "mycar") //prefix表示字首 public class Car { private String brand; private Integer price; }
#配置檔案 mycar: brand: 小鵬 price: 100000
2. 自動配置原理【總述】
SpringBoot自動配置的核心註解是@SpringBootApplication,這是個十分【重要】的註解。
@SpringBootApplication
- SpringBoot應用;
- 用在主啟動類上;
- 表名該應用是個SpringBoot應用,並且指定主啟動類入口;
- 是SpringBoot的核心註解,也是個合成註解,由3個註解組合而成(
@SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
);- SpringBoot原始碼:
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication{ …… }
主程式註解結構列圖【精髓】
建議在下面學習底層註解時對照結構圖。
@SpringBootApplication:主程式註解;
@SpringBootConfiguration
:表名核心配置類;- @Configuration:表名配置類;
@ComponentScan
:開啟包掃描;@EnableAutoConfiguration
:啟用自動配置;- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
- @Import(AutoConfigurationPackages.Registrar.class):容器中匯入Registrar元件;
- @Import(AutoConfigurationImportSelector.class):【重要】按照條件配裝規則@Conditional,按需配置;
- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
下面將對@SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
三個註解逐一分析。
3. 引導載入自動配置類【三註解原始碼分析】
這三個註解中,@EnableAutoConfiguration為核心。
@SpringBootConfiguration
- 配置註解;
- 標註類上;
- 底層是一個
@Configuration
,代表當前類是一個配置類。在這裡指核心配置類。
@ComponentScan
-
開啟包掃描;
-
標註類上;
-
可以指定掃描路徑,掃描到的包裡的註解才能生效。在這裡自定義了兩個掃描器。
- 例:
@ComponentScan(basePackages = {"com.dlhjw"})
- 例:
@EnableAutoConfiguration
- 啟用自動配置;
- 標註在配置類上;
- 需要在配置類裡寫,表示開啟屬性配置功能,將指定的元件自動註冊到容器中;
- 重要註解,實現註解
@SpringBootApplication
的核心功能,其本身也是一個合成註解。- 註解原始碼,由
@AutoConfigurationPackage
與@Import
合成:
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { …… }
- 註解原始碼,由
第一個註解:
@AutoConfigurationPackage
- 自動配置包;
- 用在類上;
- 指定了自動配置包的預設規則:將主程式類
MainApplication
所在包下的所有元件用Registrar
方法批量註冊進容器裡。- 註解原始碼:
@Import(AutoConfigurationPackages.Registrar.class) //給容器中匯入一個元件 public @interface AutoConfigurationPackage { …… }
原始碼級分析:
解釋SpringBoot預設的掃描路徑為主程式類
MainApplication
所在包及以下:
Registrar方法原始碼如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
//Registrar方法傳遞兩個引數,第一個是註解的源資訊AnnotationMetadata,這個註解標在主程式類MainApplication上(合成註解層層傳遞)
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//Registrar方法利用註解源資訊獲取到主程式MainApplication所在包名com.dlhjw.boot,封裝成陣列,註冊進容器裡。
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
- 即:
@AutoConfigurationPackage
註解的含義是:將指定的一個包下的所有元件匯入進容器。
第二個註解
@Import(AutoConfigurationImportSelector.class)【重要】
- 引入自動配置類;
- 用在類上;
- 在SpringBoot初始啟動時匯入127個自動配置類,按照條件配裝規則
@Conditional
,按需配置;
原始碼級分析:
總體上:利用Selector機制給容器批量匯入自動配置類;(底層 -> 實現)
- 首先從
META-INF/spring.factories
位置載入一個檔案。即預設掃描當前系統裡面所有META-INF/spring.factories
位置的檔案。其中最重要的是spring-boot-autoconfigure-2.3.4.RELEASE.jar包裡的META-INF/spring.factories(SpringBoot相容全場景的127個自動配置類就在這裡,即xxxxAutoConfiguration
); - 接著使用Spring的工廠載入類
loadSpringFactories
得到所有的元件; - 然後呼叫
getCandidateConfigurations()
獲取到所有需要匯入到容器中的配置類(預設匯入導容器中的127個全類名元件) - 接著利用
getAutoConfigurationEntry(annotationMetadata)
方法獲取自動配置集合 - 【核心】最後對getAutoConfigurationEntry(annotationMetadata)獲取到的配置進行封裝,封裝成
selectImports(AnnotationMetadata am)
方法,返回String陣列,陣列裡說明了需要匯入的自動配置類(元件)。
4. 按需開啟自動配置項【核心】
雖然127個場景的所有自動配置啟動的時候預設全部載入。
xxxxAutoConfiguration
但我們不一定全部都會用到,需要按照條件裝配規則@Conditional
,按需配置。
例:給容器中加入檔案上傳解析器;
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有這個型別元件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中沒有這個名字 multipartResolver 的元件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//給@Bean標註的方法傳入了物件引數,這個引數的值會從容器中找。
//SpringMVC multipartResolver。防止有些使用者配置的檔案上傳解析器不符合規範
return resolver;
}
5. 修改預設配置【定製化配置】
約定大於配置思想: SpringBoot預設會在底層配好所有的元件。但是如果使用者自己配置了以使用者的優先
@ConditionalOnMissingBean
- 條件裝配;
- 用在方法上;
- SpringBoot原始碼裡經常會有
@ConditionalOnMissingBean
註解,表示當容器中沒有該元件時,才會註冊SpringBoot預設的。//原始碼裡 @Bean @ConditionalOnMissingBean public CharacterEncodingFilter characterEncodingFilter() { }
使用者配置方式(定製化配置):
定製化配置基於屬性繫結註解
@EnableConfigurationProperties
與修改預設配置方法
-
在config包下的配置檔案類上標註引入配置檔案註解
@ImportResource
@ImportResource("classpath:beans.xml") public class MyConfig(){ //(*) }
-
在config包下的配置檔案類裡(*)處配置使用者方式
@Bean public CharacterEncodingFilter myFilter() { …… }
-
在
application.properties
配置檔案裡配置(推薦)- 檢視哪些屬性可以配置(常用應用程式屬性)
- 中文:https://www.cnblogs.com/xumBlog/p/10424351.html
- 英文:https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
- 原始碼分析:通過
@EnableConfigurationProperties
註解,找到對應的xxxProperties
類。
*6. 改變掃描路徑
SpringBoot預設掃描主程式所在包及其下面的所有子包裡面的元件,但可以改變掃描路徑。
改變掃描路徑:
@SpringBootApplication(scanBasePackages="com.dlhjw")
- 掃描com.dlhjw包下的註解;
@ComponentScan("com.dlhjw")
- 掃描com.dlhjw包下的註解;
排除自動掃描:
@SpringBootApplication(exclude={XX.class,YY.class})
- 例:
@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
取消資料庫配置。
7. 自動配置總結
一句話概括SpringBoot的自動配置原理就是:啟動時載入所有,最終按照條件進行裝配。
即:
- SpringBoot先載入所有的自動配置類
xxxxxAutoConfiguration
; - 每個自動配置類按照條件進行生效,預設都會繫結配置檔案指定的值。從
xxxxProperties
裡面取,xxxProperties
和配置檔案進行了繫結; - 生效的配置類就會給容器中裝配很多元件,只要容器中有這些元件,相當於這些功能就有了;
- 由此可得:定製化配置基於屬性繫結註解
@EnableConfigurationProperties
與修改預設配置方法- 使用者直接自己
@Bean
替換底層的元件(基於SpringBoot的@ConditionalOnMissingBean); - 使用者去看這個元件是獲取的配置檔案什麼值就去修改(基於SpringBoot的屬性繫結註解@EnableConfigurationProperties);
- 使用者直接自己
- 即:xxxxxAutoConfiguration ---> 元件 ---> xxxxProperties裡面拿值 ---> application.properties
主程式註解結構列圖
這裡再列一下結構圖,因為本小節的精髓都在這了。
@SpringBootApplication:主程式註解;
@SpringBootConfiguration
:表名核心配置類;- @Configuration:表名配置類;
@ComponentScan
:開啟包掃描;@EnableAutoConfiguration
:啟用自動配置;- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
- @Import(AutoConfigurationPackages.Registrar.class):容器中匯入Registrar元件;
- @Import(AutoConfigurationImportSelector.class):【重要】按照條件配裝規則@Conditional,按需配置;
- @AutoConfigurationPackage:自動配置包,指定了自動配置包的預設規則;
其他說明:
- 若在配置類裡只有一個有參構造器,說明有參構造器裡的所有屬性都會從屬性裡確定;(4.2.x 設定靜態頁面)