前言
自動(dòng)裝配是 Spring Boot 最核心的功能之一,第三方可以基于這個(gè)特性非常方便的和 Spring 做整合,實(shí)現(xiàn)自己的 Starter,做到開箱即用。
Java 早期并不支持注解,所以那會(huì)兒 Spring 只能通過 xml 的形式來配置。早期項(xiàng)目里要引入一個(gè)功能模塊,首先我們要引入 SDK,然后在 xml 里配置所需的 bean。隨著引入的模塊越來越多,開發(fā)者很快陷入 xml 的旋渦之中。
Spring 3.0 時(shí)代,官方開始提供@Import
注解實(shí)現(xiàn)自動(dòng)裝配的能力,同時(shí)也伴隨了一堆以@EnableXXX
命名風(fēng)格的注解,顧名思義加上這些注解就能啟用對(duì)應(yīng)的能力,這倆注解一般配合使用。
到了 Spring Boot 時(shí)代,自動(dòng)裝配再度迎來升級(jí),它在@Import
基礎(chǔ)上增加了 SPI 的能力,而且還支持條件裝配,使用上更加靈活。
理解自動(dòng)裝配
什么是自動(dòng)裝配???
跟自動(dòng)裝配相對(duì)立的就是手動(dòng)裝配,早期我們通過 xml 手動(dòng)往容器里注冊 bean 的方式就是手動(dòng)裝配。手動(dòng)裝配的缺點(diǎn)是:
- 使用麻煩,需要維護(hù)一堆 xml
- 使用門檻高,開發(fā)者需要知道配置細(xì)節(jié)
反之,自動(dòng)裝配就是開發(fā)者根據(jù) Spring Boot 定制的規(guī)范編寫AutoConfiguration
類,Spring Boot 會(huì)自動(dòng)加載這些配置類并把對(duì)應(yīng)的 bean 注冊到容器,這些 bean 是具備某種能力的,這樣第三方就可以很輕松的把自己要提供的功能裝進(jìn) Spring Boot。有了自動(dòng)裝配,開發(fā)者僅需加上少量注解或配置,甚至什么都不加(約定大于配置),就可以為項(xiàng)目引入一個(gè)功能模塊。
裝配的是什么???
從廣義上理解,裝配的是模塊、是組件、是一個(gè)具體的功能。從狹義上理解,裝配的其實(shí)是一個(gè)個(gè)具備某種能力的 bean。
自動(dòng)裝配是怎么實(shí)現(xiàn)的???
通過一個(gè)叫@EnableAutoConfiguration
的注解往容器導(dǎo)入了一個(gè)叫AutoConfigurationImportSelector
的類,它實(shí)現(xiàn)了ImportSelector
接口,Spring 啟動(dòng)時(shí)會(huì)觸發(fā)子類方法按照規(guī)則加載自動(dòng)裝配類。
設(shè)計(jì)實(shí)現(xiàn)
在使用 Spring Boot 開發(fā)時(shí),我們一般會(huì)在啟動(dòng)類上加@SpringBootApplication
注解,它就是自動(dòng)裝配的入口。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
它是一個(gè)復(fù)合注解,里面還集成了 Spring 提供的一些其它注解,從名字就能看出來,與自動(dòng)裝配有關(guān)的是@EnableAutoConfiguration
注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@EnableAutoConfiguration
也是一個(gè)復(fù)合注解,它最核心的功能就是往容器導(dǎo)入了AutoConfigurationImportSelector
類。
AutoConfigurationImportSelector 實(shí)現(xiàn)了各種 Aware 接口,具備 BeanFactory、BeanClassLoader 等感知能力。最重要的是它實(shí)現(xiàn)了 DeferredImportSelector 接口,DeferredImportSelector 又繼承自 ImportSelector。
ImportSelector 接口用來向容器注冊批量導(dǎo)入配置類,子類重寫selectImports()
返回要導(dǎo)入的類的全限定名數(shù)組:
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
子接口 DeferredImportSelector 的區(qū)別是二者導(dǎo)入的時(shí)機(jī)不同,ImportSelector 會(huì)在@Configuration
Bean 處理前調(diào)用,DeferredImportSelector 會(huì)等處理完所有的@Configuration
Bean 之后再調(diào)用。
所以,Spring 啟動(dòng)時(shí)會(huì)先觸發(fā)AutoConfigurationImportSelector.AutoConfigurationGroup#process
收集要導(dǎo)入的類,再觸發(fā)selectImports()
返回導(dǎo)入項(xiàng)的迭代器。
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 獲取要導(dǎo)入的自動(dòng)配置類
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
核心是getAutoConfigurationEntry()
,它會(huì)按照 Spring Boot 的規(guī)范加載自動(dòng)裝配類,去重后再移除掉需要被排除的類,接著觸發(fā)一個(gè) AutoConfigurationImportListener 監(jiān)聽事件,最后返回收集到的類。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
// 可以通過配置spring.boot.enableautoconfiguration=false來禁用自動(dòng)裝配
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 加載候選配置類
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重 轉(zhuǎn)HashSet
configurations = removeDuplicates(configurations);
// 移除掉要排除的類
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
// 觸發(fā)AutoConfigurationImportListener監(jiān)聽事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
Spring Boot 會(huì)按照什么規(guī)則加載配置類呢?方法是getCandidateConfigurations()
,它會(huì)加載候選的配置類,有兩套加載規(guī)則:
- 讀取 ClassPath 下
META-INF/spring.factories
文件里以org.springframework.boot.autoconfigure.EnableAutoConfiguration
為 Key 配置的類 - 讀取 ClassPath 下
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件里配置的所有類
第一套規(guī)則更多的是給第三方提供的口子,第二套規(guī)則是 Spring Boot 導(dǎo)入內(nèi)部配置類的口子。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
/**
* 讀取ClassPath下META-INF/spring.factories文件里
* 被org.springframework.boot.autoconfigure.EnableAutoConfiguration標(biāo)注的類
*/
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
/**
* 讀取ClassPath下META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
* 文件里配置的類都要導(dǎo)入
*/
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
return configurations;
}
讀取文件加載配置類用到了 Spring 提供的 SpringFactoriesLoader 類,代碼不復(fù)雜,這里就不贅述了。
我們重點(diǎn)看一下,Spring Boot 內(nèi)部都提供了哪些配置類,文件路徑在spring-boot-autoconfigure/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
,官方默認(rèn)提供了 144 個(gè)自動(dòng)裝配類,這里貼幾個(gè)示例。
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
......
這些配置類基本從名字就可以看出它們的功能,例如:AopAutoConfiguration 用來配置切面編程相關(guān)的 bean;RabbitAutoConfiguration 用來配置 RabbitMQ 相關(guān)的 bean。
自動(dòng)配置類本身會(huì)作為 bean 注冊到容器,除此之外還可以通過@Bean
注解方法的形式注冊 bean。如果要提供的功能比較復(fù)雜,不想所有的代碼都耦合在一個(gè)類里面,還可以在配置類上繼續(xù)加@Import
注解引入另一個(gè)配置類。
條件配置
官方一股腦提供了 144 個(gè)自動(dòng)配置類,我們不一定都需要啊,所以條件配置誕生了。
條件配置的意思是,可以給自動(dòng)配置類加上一些前置條件,只有這些條件都滿足了,配置類才會(huì)生效。條件配置伴隨的是一堆以@ConditionalXXX
命名風(fēng)格的注解,這里舉幾個(gè)常用的:
- @ConditionalOnBean:容器存在滿足條件的 bean 才生效
- @ConditionalOnClass:容器存在滿足條件的 Class 才生效
- @ConditionalOnMissingBean:容器不存在滿足條件的 bean 才生效
- @ConditionalOnMissingClass:容器不存在滿足條件的 Class 才生效
- @ConditionalOnWebApplication:必須是指定類型的 Web 應(yīng)用環(huán)境才生效
有了條件配置,即使官方一股腦提供了一堆自動(dòng)配置類,很多也都是不會(huì)生效的,要想生效我們得引入相關(guān)的依賴和配置。
以 RabbitAutoConfiguration 為例,它的生效條件是存在 RabbitTemplate.class、Channel.class,如果我們沒有引入相關(guān)依賴,這些類肯定是不存在的,配置類自然也就不會(huì)生效了。文章來源:http://www.zghlxwxcb.cn/news/detail-817769.html
@AutoConfiguration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties(RabbitProperties.class)
@Import({ RabbitAnnotationDrivenConfiguration.class, RabbitStreamConfiguration.class })
public class RabbitAutoConfiguration {
}
尾巴
所謂的自動(dòng)裝配,就是 Spring Boot 程序在啟動(dòng)時(shí)會(huì)去掃描 ClassPath 下的META-INF/spring.factories
文件,自動(dòng)把 AutoConfiguration 類注冊到容器,免去了開發(fā)者需要自己配置 bean 的麻煩過程。Spring Boot 一股腦提供了 144 個(gè)自動(dòng)配置類,絕大多數(shù)開發(fā)者并不需要,所以提供了基于條件的自動(dòng)配置,只有前置條件都滿足了,配置類才會(huì)生效。基于自動(dòng)裝配和約定大于配置的設(shè)計(jì)理念,開發(fā)者僅需編寫少量配置甚至不寫任何配置就可以方便的引入一個(gè)功能模塊。文章來源地址http://www.zghlxwxcb.cn/news/detail-817769.html
到了這里,關(guān)于Spring Boot自動(dòng)裝配的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!