1. 起因 使用springboot也有些時間,一直很好奇它如何做到自動配置的,所以查閱了相關資料並且學習了相關內容,才寫了這篇文章。 2. 分析 ①第一步我們從它的啟動配置類(XxxApplication)收起,我們進入到他的@SpringBootApplication註解。 ②我們可 ...
1. 起因
使用springboot也有些時間,一直很好奇它如何做到自動配置的,所以查閱了相關資料並且學習了相關內容,才寫了這篇文章。
2. 分析
①第一步我們從它的啟動配置類(XxxApplication)收起,我們進入到他的@SpringBootApplication註解。
②我們可以看到如下代碼,由於我們需要找到導致它自動配置的,所以鎖定了@EnableAutoConfiguration註解,那麼就可以進入這個註解。
@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 {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
③ 我們看到瞭如下代碼,由於這是一個介面,而它的實現類我們又不好確定,所以我們只好從註解入手,由於@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited等都是元註解,我們可以從@AutoConfigurationPackage,@Import({AutoConfigurationImportSelector.class})中找到我們想要的,根據它的中文意思我們可以從@Import({AutoConfigurationImportSelector.class})(自動有選擇的導入配置)出發,畢竟不是所有的配置都會都如,而導入那些配置根據項目中使用了那些starter決定。
④點入第三步提到的註解,進入這個類根據方法名我們可以猜測是和這個方法相關,通過閱讀這個代碼,我們可以猜測是和getAutoConfigurationEntry這個方法有關,所以不妨點進去。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
⑤由於我們需要的配置Config,所以大致可以斷定是和這個代碼有關聯List
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
⑥經過第五步,我們可以看到如下代碼
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
不妨看這部分代碼,List
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
看到這裡可能還是會疑惑?為啥就自動配置了,其實我們這個方法(loadFactoryNames)是從一個配置文件中讀取內容,它的鍵是EnableAutoConfiguration,而這個配置文件是在如圖所示的文件中。
⑥打開上述文件,根據上一步的鍵找它的值,如圖就是springboot配置類,那麼項目啟動時是不是所有的配置都起作用嗎?不妨進入到某個配置類查看即可。如圖所示的@ConditionalOnMissingBean註解表示項目的容器是否有這個bean,如果沒有這個配置類就不會其作用,另外我們只要在仔細看這個類首先進行了初始化,之後它會從配置文件(application.xml或者application.yml)中獲取值。
註:如有錯誤,歡迎指出。