本節書摘來自華章出版社《springboot揭秘:快速建構微服務體系》一書中的第3章,第3.2節@springbootapplication背後的秘密,作者王福強,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
3.2 @springbootapplication背後的秘密
@springbootapplication是一個“三體”結構,實際上它是一個複合annotation:
雖然它的定義使用了多個annotation進行元資訊标注,但實際上對于springboot應用來說,重要的隻有三個annotation,而“三體”結構實際上指的就是這三個annotation:
@configuration
@enableautoconfiguration
@componentscan
是以,如果我們使用如下的springboot啟動類,整個springboot應用依然可以與之前的啟動類功能對等:
但每次都寫三個annotation顯然過于繁瑣,是以寫一個@springboot-application這樣的一站式複合annotation顯然更友善些。
3.2.1 @configuration創世紀
這裡的@configuration對我們來說并不陌生,它就是javaconfig形式的spring ioc容器的配置類使用的那個@configuration,既然springboot應用骨子裡就是一個spring應用,那麼,自然也需要加載某個ioc容器的配置,而springboot社群推薦使用基于javaconfig的配置形式,是以,很明顯,這裡的啟動類标注了@configuration之後,本身其實也是一個ioc容器的配置類!
很多springboot的代碼示例都喜歡在啟動類上直接标注@configuration或者@springbootapplication,對于初接觸springboot的開發者來說,其實這種做法不便于了解,如果我們将上面的springboot啟動類拆分為兩個獨立的java類,整個形勢就明朗了:
是以,啟動類demoapplication其實就是一個标準的standalone類型java程式的main函數啟動類,沒有什麼特殊的。
而@configuration标注的democonfiguration定義其實也是一個普通的javaconfig形式的ioc容器配置類,沒啥新東西,全是spring架構裡的概念!
3.2.2 @enableautoconfiguration的功效
@enableautoconfiguration其實也沒啥“創意”,各位是否還記得spring架構提供的各種名字為@enable開頭的annotation定義?比如@enablescheduling、@enablecaching、@enablembeanexport等,@enableautoconfiguration的理念和“做事方式”其實一脈相承,簡單概括一下就是,借助@import的支援,收集和注冊特定場景相關的bean定義:
@enablescheduling是通過@import将spring排程架構相關的bean定義都加載到ioc容器。
@enablembeanexport是通過@import将jmx相關的bean定義加載到ioc容器。
而@enableautoconfiguration也是借助@import的幫助,将所有符合自動配置條件的bean定義加載到ioc容器,僅此而已!
@enableautoconfiguration作為一個複合annotation,其自身定義關鍵資訊如下:
其中,最關鍵的要屬@import(enableautoconfigurationimportselector.class),借助enableautoconfigurationimportselector,@enableautoconfiguration可以幫助springboot應用将所有符合條件的@configuration配置都加載到目前springboot建立并使用的ioc容器,就跟一隻“八爪魚”一樣(如圖3-1所示)。
借助于spring架構原有的一個工具類:springfactoriesloader的支援,@enableautoconfiguration可以“智能”地自動配置功效才得以大功告成!

自動配置的幕後英雄:springfactoriesloader詳解
springfactoriesloader屬于spring架構私有的一種擴充方案(類似于java的spi方案java.util.serviceloader),其主要功能就是從指定的配置檔案meta-inf/spring.factories加載配置,spring.factories是一個典型的java properties檔案,配置的格式為key = value形式,隻不過key和value都是java類型的完整類名(fully qualified name),比如:
example.myservice=example.myserviceimpl1,example.myserviceimpl2
然後架構就可以根據某個類型作為key來查找對應的類型名稱清單了:
對于@enableautoconfiguration來說,springfactoriesloader的用途稍微不同一些,其本意是為了提供spi擴充的場景,而在@enableautoconfiguration的場景中,它更多是提供了一種配置查找的功能支援,即根據@enableautoconfiguration的完整類名org.springframework.boot.autoconfigure.enableautoconfiguration作為查找的key,擷取對應的一組@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配置檔案中摘錄的一段内容,可以很好地說明問題。
是以,@enableautoconfiguration自動配置的魔法其實就變成了:從classpath中搜尋所有meta-inf/spring.factories配置檔案,并将其中org.spring-framework.boot.autoconfigure.enableautoconfiguration對應的配置項通過反射(java reflection)執行個體化為對應的标注了@configuration的javaconfig形式的ioc容器配置類,然後彙總為一個并加載到ioc容器。
目前為止,還是spring架構的原有概念和支援,依然沒有“新鮮事”!
3.2.3 可有可無的@componentscan
為啥說@componentscan是可有可無的?因為原則上來說,作為spring架構裡的“老一輩革命家”,@componentscan的功能其實就是自動掃描并加載符合條件的元件或bean定義,最終将這些bean定義加載到容器中。加載bean定義到spring的ioc容器,我們可以手工單個注冊,不一定非要通過批量的自動掃描完成,是以說@componentscan是可有可無的。
對于springboot應用來說,同樣如此,比如我們本章的啟動類:
如果我們目前應用沒有任何bean定義需要通過@componentscan加載到目前springboot應用對應使用的ioc容器,那麼,除去@componentscan的聲明,目前springboot應用依然可以照常運作,功能對等!
看,還是沒有啥新東西!