SpringBoot中@SpringBootApplication注解的三體結(jié)構(gòu)解析

@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 所示)仇味。

EnableAutoConfiguration得以生效的關(guān)鍵組件關(guān)系圖

圖 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ì)等乖阵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞪浸,一起剝皮案震驚了整個(gè)濱河市对蒲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蹈矮,老刑警劉巖泛鸟,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件北滥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡菊霜,警方通過(guò)查閱死者的電腦和手機(jī)鉴逞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門华蜒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人贺拣,你說(shuō)我怎么就攤上這事捂蕴∩侗妫” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)腕够。 經(jīng)常有香客問(wèn)我帚湘,道長(zhǎng)玫荣,這世上最難降的妖魔是什么捅厂? 我笑而不...
    開(kāi)封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮贿堰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘沸手。我一直安慰自己,他們只是感情好契吉,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布捐晶。 她就那樣靜靜地躺著惑灵,像睡著了一般英支。 火紅的嫁衣襯著肌膚如雪哮伟。 梳的紋絲不亂的頭發(fā)上妄帘,一...
    開(kāi)封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音池凄,去河邊找鬼抡驼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肿仑,可吹牛的內(nèi)容都是我干的致盟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼尤慰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼馏锡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起割择,我...
    開(kāi)封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤眷篇,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后荔泳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蕉饼,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年创肥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹侄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖飘哨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情臼闻,我是刑警寧澤惩淳,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響学辱,放射性物質(zhì)發(fā)生泄漏衙傀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一金麸、第九天 我趴在偏房一處隱蔽的房頂上張望钱骂。 院中可真熱鬧,春花似錦解取、人聲如沸蔓肯。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耻矮。三九已至,卻和暖如春哨免,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踪危。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袱结,于是被迫代替她去往敵國(guó)和親溢吻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359