@SpringBootApplication 是一個(gè)“三體”結(jié)構(gòu),實(shí)際上它是一個(gè)復(fù)合 Annotation:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic
@interface
SpringBootApplication{...}
雖然它的定義使用了多個(gè) Annotation 進(jìn)行元信息標(biāo)注虚青,但實(shí)際上對(duì)于 SpringBoot 應(yīng)用來(lái)說(shuō)挟憔,重要的只有三個(gè) Annotation烟号,而“三體”結(jié)構(gòu)實(shí)際上指的就是這三個(gè) Annotation:
- @Configuration
- @EnableAutoConfiguration
- @ComponentScan
所以,如果我們使用如下的 SpringBoot 啟動(dòng)類达传,整個(gè) SpringBoot 應(yīng)用依然可以與之前的啟動(dòng)類功能對(duì)等:
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic
class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
但每次都寫三個(gè) Annotation 顯然過(guò)于繁瑣宪赶,所以寫一個(gè) @SpringBootApplication 這樣的一站式復(fù)合 Annotation 顯然更方便些搂妻。
@Configuration 創(chuàng)世紀(jì)
這里的 @Configuration 對(duì)我們來(lái)說(shuō)并不陌生,它就是 JavaConfig 形式的 Spring IoC 容器的配置類使用的那個(gè) @Configuration邓厕,既然 SpringBoot 應(yīng)用骨子里就是一個(gè) Spring 應(yīng)用详恼,那么引几,自然也需要加載某個(gè) IoC 容器的配置,而 SpringBoot 社區(qū)推薦使用基于 JavaConfig 的配置形式敞掘,所以渐逃,很明顯民褂,這里的啟動(dòng)類標(biāo)注了 @Configuration 之后赊堪,本身其實(shí)也是一個(gè) IoC 容器的配置類竖哩!
很多 SpringBoot 的代碼示例都喜歡在啟動(dòng)類上直接標(biāo)注 @Configuration 或者 @SpringBootApplication相叁,對(duì)于初接觸 SpringBoot 的開(kāi)發(fā)者來(lái)說(shuō),其實(shí)這種做法不便于理解椿访,如果我們將上面的 SpringBoot 啟動(dòng)類拆分為兩個(gè)獨(dú)立的 Java 類成玫,整個(gè)形勢(shì)就明朗了:
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class DemoConfiguration {
@Bean
public Controller controller() {
return new Controller();
}
}
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoConfiguration.class, args);
}
}
所以,啟動(dòng)類 DemoApplication 其實(shí)就是一個(gè)標(biāo)準(zhǔn)的 Standalone 類型 Java 程序的 main 函數(shù)啟動(dòng)類猪腕,沒(méi)有什么特殊的陋葡。而 @Configuration 標(biāo)注的 DemoConfiguration 定義其實(shí)也是一個(gè)普通的 JavaConfig 形式的 IoC 容器配置類彻采。
@EnableAutoConfiguration 的功效
@EnableAutoConfiguration 其實(shí)也沒(méi)啥“創(chuàng)意”颊亮,各位是否還記得 Spring 框架提供的各種名字為 @Enable 開(kāi)頭的 Annotation 定義?
比如 @EnableScheduling绍在、@EnableCaching雹有、@EnableMBeanExport 等,@EnableAutoConfiguration 的理念和“做事方式”其實(shí)一脈相承溜宽,簡(jiǎn)單概括一下就是适揉,借助 @Import 的支持煤惩,收集和注冊(cè)特定場(chǎng)景相關(guān)的 bean 定義:
- @EnableScheduling 是通過(guò) @Import 將 Spring 調(diào)度框架相關(guān)的 bean 定義都加載到 IoC 容器。
- @EnableMBeanExport 是通過(guò) @Import 將 JMX 相關(guān)的 bean 定義加載到 IoC 容器剪侮。
而 @EnableAutoConfiguration 也是借助 @Import 的幫助洛退,將所有符合自動(dòng)配置條件的 bean 定義加載到 IoC 容器兵怯,僅此而已!
@EnableAutoConfiguration 作為一個(gè)復(fù)合 Annotation摇零,其自身定義關(guān)鍵信息如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
其中,最關(guān)鍵的要屬 @Import(EnableAutoConfigurationImportSelector.class)谅畅,借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以幫助 SpringBoot 應(yīng)用將所有符合條件的 @Configuration 配置都加載到當(dāng)前 SpringBoot 創(chuàng)建并使用的 IoC 容器胜茧,就跟一只“八爪魚”一樣(如圖 1 所示)仇味。
圖 1 EnableAutoConfiguration得以生效的關(guān)鍵組件關(guān)系圖
借助于 Spring 框架原有的一個(gè)工具類:SpringFactoriesLoader 的支持廊遍,@EnableAutoConfiguration 可以“智能”地自動(dòng)配置功效才得以大功告成贩挣!
SpringFactoriesLoader詳解
SpringFactoriesLoader 屬于 Spring 框架私有的一種擴(kuò)展方案(類似于 Java 的 SPI 方案 java.util.ServiceLoader),其主要功能就是從指定的配置文件 META-INF/spring.factories 加載配置卵迂,spring.factories 是一個(gè)典型的 java properties 文件绒净,配置的格式為 Key=Value 形式,只不過(guò) Key 和 Value 都是 Java 類型的完整類名(Fully qualified name)改览,比如:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2 然后框架就可以根據(jù)某個(gè)類型作為 Key 來(lái)查找對(duì)應(yīng)的類型名稱列表了:
public abstract class SpringFactoriesLoader {
// ...
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
...
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
...
}
// ...
}
對(duì)于 @EnableAutoConfiguration 來(lái)說(shuō)恃疯,SpringFactoriesLoader 的用途稍微不同一些墨闲,其本意是為了提供 SPI 擴(kuò)展的場(chǎng)景鸳碧,而在 @EnableAutoConfiguration 的場(chǎng)景中犬性,它更多是提供了一種配置查找的功能支持,即根據(jù) @EnableAutoConfiguration 的完整類名 org.springframework.boot.autoconfigure.EnableAutoConfiguration 作為查找的 Key套利,獲取對(duì)應(yīng)的一組 @Configuration 類:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
\org.springframework.boot.autoconfigure.admin.SpringApplicationAdmin- JmxAutoConfiguration,
\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
\org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,
\org.springframework.boot.autoconfigure.PropertyPlaceholderAuto- Configuration,
\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
\org.springframework.boot.autoconfigure.cassandra.CassandraAuto-Configuration,
\org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
\org.springframework.boot.autoconfigure.context.ConfigurationProperties-AutoConfiguration,
\org.springframework.boot.autoconfigure.dao.PersistenceException-TranslationAutoConfiguration,
\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-DataAutoConfiguration,
\org.springframework.boot.autoconfigure.data.cassandra.Cassandra-RepositoriesAutoConfiguration,
\...
以上是從 SpringBoot 的 autoconfigure 依賴包中的 META-INF/spring.factories 配置文件中摘錄的一段內(nèi)容验辞,可以很好地說(shuō)明問(wèn)題喊衫。
所以,@EnableAutoConfiguration 自動(dòng)配置的魔法其實(shí)就變成了:從 classpath 中搜尋所有 META-INF/spring.factories 配置文件壳贪,并將其中 org.spring-framework.boot.autoconfigure.EnableAutoConfiguration 對(duì)應(yīng)的配置項(xiàng)通過(guò)反射(Java Reflection)實(shí)例化為對(duì)應(yīng)的標(biāo)注了 @Configuration 的 JavaConfig 形式的 IoC 容器配置類违施,然后匯總為一個(gè)并加載到 IoC 容器磕蒲。
可有可無(wú)的@ComponentScan
為啥說(shuō) @ComponentScan 是可有可無(wú)的?
因?yàn)樵瓌t上來(lái)說(shuō)亿卤,作為 Spring 框架里的“老一輩革命家”排吴,@ComponentScan 的功能其實(shí)就是自動(dòng)掃描并加載符合條件的組件或 bean 定義懦鼠,最終將這些 bean 定義加載到容器中。加載 bean 定義到 Spring 的 IoC 容器肛冶,我們可以手工單個(gè)注冊(cè)睦袖,不一定非要通過(guò)批量的自動(dòng)掃描完成,所以說(shuō) @ComponentScan 是可有可無(wú)的馅笙。
對(duì)于 SpringBoot 應(yīng)用來(lái)說(shuō),同樣如此烈和,比如我們本章的啟動(dòng)類:
@Configuration
@EnableAutoConfiguration
@ComponentScanpublic
class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
如果我們當(dāng)前應(yīng)用沒(méi)有任何 bean 定義需要通過(guò) @ComponentScan 加載到當(dāng)前 SpringBoot 應(yīng)用對(duì)應(yīng)使用的 IoC 容器招刹,那么恬试,除去 @ComponentScan 的聲明训柴,當(dāng)前 SpringBoot 應(yīng)用依然可以照常運(yùn)行畦粮,功能對(duì)等乖阵。