SpringBoot自動配置總結

原始博文鏈接

前言

眼下SpringBoot算是比較熱門的一塊,手頭的小項目正好也是SpringBoot悴灵,于是打算總結下SpringBoot的核心特性:自動配置。雖然這部分知識并不深捺信,網上也是一搜一大把耕挨,不過個人感覺還是值得一寫搔驼。本文內容基于SpringBoot 2.0.4版本厢塘。

緣由雜談

新技術的流行總是因為解決了某個痛點而進入視野茶没。Spring強大的解耦能力可以說塑造了一個“王朝”,一個框架能夠對一種語言產生如此深刻的“糾葛”應該是非常少見了晚碾。隨后就是各種組件的集成抓半,幾乎所有java系組件都能夠找到與Spring整合的例子,而整合帶來了大量的以XML為主要格式的配置文件迄薄,處理配置配置文件的時候琅关,最常用的手法就是所謂的“CV大法“煮岁,痛點就來源于此讥蔽。

為了減少繁雜的配置文件,從Spring3.0開始画机,出現了注解@Configuration用于定義配置類冶伞,用配置類的形式代替XML配置文件。配置類帶來了不少好處步氏,比如更加易讀响禽、可配置生效條件等,但是并沒有完全取代配置文件,應該說是相輔相成芋类,而且配置并沒有減少隆嗅,只是換了個形式。

既然配置時存在大量的復制粘貼侯繁,那么何不弄成自動配置和所謂的OOTB胖喳?想必SpringBoot構建的出發(fā)點與這種想法有相似的考量。

約定優(yōu)于配置

配置少了并不代表配置消失了贮竟,簡化往往伴隨著約定丽焊,SpringBoot也是如此。SpringBoot的應用入口點就是注解了@SpringBootApplication的主類的main方法咕别,初次使用時SpringBoot時往往會驚艷于簡單的一個main方法就能跑起整個應用技健。作為框架來說,簡單是優(yōu)點惰拱,可配是核心雌贱,同樣的,自動配置這個特性也是可配的偿短。

簡單描述下SpringBoot自動配置生效的過程:@SpringBootApplication實際為一個復合注解帽芽,包括了@Configuration@EnableAutoConfiguration翔冀、@ComponentScan导街,都與自動配置有關,但開啟自動配置主要由@EnableAutoConfiguration來完成纤子。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

@Import注解注入了EnableAutoConfigurationImportSelector類搬瑰,這個類使用SpringFactoriesLoader.loadFactoryNames方法讀取 ClassPath 目錄下面的 META-INF/spring.factories 文件,從中獲取到EnableAutoConfiguration.class類(類名)對應的值控硼,然后把他們添加在容器中泽论。

# spring.factories文件一瞥

# ... 
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
# ....

SpringBoot就通過這種“約定”的方式讀取所有的自動配置類。相當于框架的創(chuàng)建者或者自動配置包的創(chuàng)建者有能力“幫助”使用者在Spring容器中添加配置類卡乾。

自動配置類

具體的自動配置就由一個個自動配置類來完成翼悴,它們的名稱都約定為XXXAutoConfiguration。

配置注解

本質上來說自動配置類仍然是一個配置類幔妨,原理也是配置類的原理鹦赎,因此自動配置類上必然包括@Configuration這個注解。

條件注解

而自動配置的情況下误堡,由于配置類是“推送”過來的古话,必須要限制配置類的生效條件和生效的先后順序以及主次區(qū)分,確保對容器內容的控制锁施。為了實現配置的可控性陪踩,基于Spring4.0新增加的注解@Conditional杖们,SpringBoot提供了多個注解來描述配置生效條件,幾個主要的條件注解解釋如下:

<style>
table th:first-of-type {
width: 40%;
}
</style>

注解 作用(判斷是否滿足當前指定條件)
@ConditionalOnJava 系統(tǒng)的java版本是否符合要求
@ConditionalOnBean 容器中存在指定Bean
@ConditionalOnMissingBean 容器中不存在指定Bean
@ConditionalOnExpression 滿足SpEL表達式指定
@ConditionalOnClass 系統(tǒng)中有指定的類
@ConditionalOnMissingClass 系統(tǒng)中沒有指定的類
@ConditionalOnProperty 系統(tǒng)中指定的屬性是否有指定的值
@ConditionalOnWebApplication 當前是web環(huán)境
... ...

幾個主要的順序控制注解如下:

注解 作用(判斷先后順序)
@AutoConfigureBefore 指定在某個別的自動配置類之前進行配置
@AutoConfigureAfter 指定在某個別的自動配置類之后進行配置
@AutoConfigureOrder 指定自動配置的排序系數(越小越先執(zhí)行)

屬性注解

自動配置并不是完全消除配置文件肩狂,在自動配置的“過濾”下摘完,配置文件可以專心指定參數類、選項類的屬性內容傻谁,這些內容在面向對象思想的指導下又可以映射到所謂的配置屬性類的成員值上描焰。為了完成、簡化這個映射栅螟,SpringBoot提供了注解@ConfigurationProperties荆秦,只要注解在配置類上就可以根據前綴將配置文件中的內容快速映射到實體中。

同時力图,為了增強自動配置類的可讀性和屬性類實體快速加入容器步绸,SpringBoot同時提供了注解@EnableConfigurationProperties,注解在自動配置類上吃媒,快速將屬性類引入容器瓤介,配置類就可以被各個組件靈活使用。

引入注解

如果組件比較龐大赘那,或者說組件包含了各種可定制使用的子模塊刑桑,也可以將功能配置拆分在不同的配置類中,然后在自動配置類上統(tǒng)一使用注解@Import引入募舟,以獲取更清晰的層次結構和更好的閱讀體驗祠斧。使用@Import引入配置類時,配置類上的@Configuration不是必要的拱礁。

概覽

以上幾種注解組成了自動配置類的基本元數據琢锋,以HibernateJpa自動配置為例:

@Configuration
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class })
@Conditional(HibernateEntityManagerCondition.class)
@EnableConfigurationProperties(JpaProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {

    @Order(Ordered.HIGHEST_PRECEDENCE + 20)
    static class HibernateEntityManagerCondition extends SpringBootCondition {

        private static final String[] CLASS_NAMES = {
                "org.hibernate.ejb.HibernateEntityManager",
                "org.hibernate.jpa.HibernateEntityManager" };

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
                AnnotatedTypeMetadata metadata) {
            ConditionMessage.Builder message = ConditionMessage
                    .forCondition("HibernateEntityManager");
            for (String className : CLASS_NAMES) {
                if (ClassUtils.isPresent(className, context.getClassLoader())) {
                    return ConditionOutcome
                            .match(message.found("class").items(Style.QUOTE, className));
                }
            }
            return ConditionOutcome.noMatch(message.didNotFind("class", "classes")
                    .items(Style.QUOTE, Arrays.asList(CLASS_NAMES)));
        }

    }

}

啟動器

為了達到快速自動配置的目的,還需要一個便捷的依賴引入方式呢灶,而這就是啟動器(starter)的能力所在吴超。啟動器會把所有用到的依賴都給包含進來,避免了開發(fā)者自己去引入依賴所帶來的麻煩鸯乃。啟動器構成了自動配置的最后一塊拼圖鲸阻,引入啟動器就可以使得一個組件進入可用狀態(tài)。

官方啟動器

SpringBoot官方提供了大量的自動配置類和啟動器缨睡,整合了市面上的主流組件鸟悴。

官方提供的自動配置類和屬性類均在名為spring-boot-autoconfigure的包下,如果需要使用某項組件宏蛉,在依賴管理工具中引入對應的starter即可遣臼,官方提供的啟動器名稱統(tǒng)一約定為spring-boot-starter-xxx性置。在自動配置類集中放置拾并、starter按需引入的結構下,官方啟動器本身不包含任何類只用作依賴的引入。

自定義啟動器

如果SpringBoot官方沒有提供對應的啟動器嗅义,組件開發(fā)者也可以創(chuàng)建第三方自定義啟動器屏歹。自定義啟動器往往是一個獨立的項目(jar包),本身引入之碗、控制組件所需依賴蝙眶,自動配置類和屬性類也放在這個包下,并且創(chuàng)建對應的META_INF/spring.factories文件褪那,這樣使用者同樣可以通過引入這個啟動器達到自動配置的效果幽纷。第三方的啟動器的名稱一般約定為xxx-spring-boot-starter。

概覽

自動配置本質上來說還是以@Configuration這個注解的功效作為主要表現形式博敬,通過約定讀取位置代替之前指定掃包路徑的形式將配置類引入容器進行配置友浸。通過@ConfigurationProperties綁定配置類和配置文件中的屬性,可以快速定制一些基本的參數偏窝,并且提供默認的參數值收恢。這樣基本就達到了傻瓜式OOTB的效果。

豐富的條件注解可以使得在良好的設計下祭往,容器內Bean的引入都是可控的伦意,也能夠支持深度定制,這也是為什么自動配置類總是注解了大量的@ConditionalOnMissingBean硼补、@ConditionalOnClass驮肉,包含了各種各樣的Customizer、Adapter類的原因已骇。

盡管自動配置原理并不復雜缆八,但是依然可以看到各種各樣的花式配置,套了一堆靜態(tài)內部配置類讓人摸不著頭腦疾捍。把握好核心內容對框架的理解和應用是大有裨益的奈辰。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市乱豆,隨后出現的幾起案子奖恰,更是在濱河造成了極大的恐慌,老刑警劉巖宛裕,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑟啃,死亡現場離奇詭異,居然都是意外死亡揩尸,警方通過查閱死者的電腦和手機蛹屿,發(fā)現死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來岩榆,“玉大人错负,你說我怎么就攤上這事坟瓢。” “怎么了犹撒?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵折联,是天一觀的道長。 經常有香客問我识颊,道長诚镰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任祥款,我火速辦了婚禮清笨,結果婚禮上,老公的妹妹穿的比我還像新娘刃跛。我一直安慰自己函筋,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布奠伪。 她就那樣靜靜地躺著跌帐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绊率。 梳的紋絲不亂的頭發(fā)上谨敛,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音滤否,去河邊找鬼脸狸。 笑死,一個胖子當著我的面吹牛藐俺,可吹牛的內容都是我干的炊甲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼欲芹,長吁一口氣:“原來是場噩夢啊……” “哼卿啡!你這毒婦竟也來了?” 一聲冷哼從身側響起菱父,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤颈娜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后浙宜,有當地人在樹林里發(fā)現了一具尸體官辽,經...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年粟瞬,在試婚紗的時候發(fā)現自己被綠了同仆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡裙品,死狀恐怖俗批,靈堂內的尸體忽然破棺而出俗或,到底是詐尸還是另有隱情,我是刑警寧澤扶镀,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布蕴侣,位于F島的核電站焰轻,受9級特大地震影響臭觉,放射性物質發(fā)生泄漏。R本人自食惡果不足惜辱志,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一蝠筑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揩懒,春花似錦什乙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至智亮,卻和暖如春忆某,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阔蛉。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工弃舒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人状原。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跪者。 傳聞我的和親對象是個殘疾皇子玷或,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內容