前言
眼下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)內部配置類讓人摸不著頭腦疾捍。把握好核心內容對框架的理解和應用是大有裨益的奈辰。