SpringBoot自定義starter可參考:SpringBoot自動配置(一)-- 自定義starter
SpringBoot啓動過程可參考:SpringBoot 啓動流程源碼筆記
SpringBoot啓動過程會創建應用上下文ApplicationContext,類型一般是AnnotationConfigServletWebServerApplicationContext(servlet的web應用),
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
...
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
構造方法中創建了一個AnnotatedBeanDefinitionReader,而這個類的構造方法最終會調用AnnotationConfigUtils.registerAnnotationConfigProcessors,這就向容器注入了註解處理器(可參考:Spring component-scan源碼分析(二) – @Configuration註解處理)。
接着會把啓動類封裝成AnnotatedGenericBeanDefinition註冊到容器中,因爲啓動類上會用@SpringBootApplication註解裝飾
@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 {...}
其中@SpringBootConfiguration註解帶@Configuration註解的,所以註解處理器會把啓動類作爲配置類進行處理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
既然當做配置類來處理,那就要處理類上面的註解,可以看到註解有@EnableAutoConfiguration、@ComponentScan。@EnableAutoConfiguration是起着自動配置作用的註解,而@ComponentScan註解指定掃描規則,如果不設置basePackage屬性,那就默認掃描類的所在包以及子包的類。
接下來分析@EnableAutoConfiguration註解如何實現自動配置功能
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {...}
這裏的@Import註解是引入其他配置類,來看看AutoConfigurationImportSelector類的結構
可以看到它繼承了DeferredImportSelector接口,而DeferredImportSelector接口又是繼承ImportSelector接口的。Spring處理引入配置的時候,遇到實現了ImportSelector接口的類,會調用接口的selectImports方法來拿到需要引入的類名數組進行解析引入。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
}
而如果對於實現了DeferredImportSelector接口類,Spring是會在處理完其他所有配置類都解析完成後,再解析這個類(這便於處理條件註解@ConditionalOnBean、ConditionalOnMissingBean等)。
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends DeferredImportSelector.Group> getImportGroup() {
return null;
}
public interface Group {
void process(AnnotationMetadata var1, DeferredImportSelector var2);
Iterable<DeferredImportSelector.Group.Entry> selectImports();
//內部靜態類,裝有元數據和類名
public static class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
...
}
}
}
DeferredImportSelector這個接口在Spring5增加了內部接口Group,Spring5處理DeferredImportSelector的時候會先調用getImportGroup拿到Group類型的類,然後實例化這個類,接着調用process方法,再調用selectImports拿到要引入的配置集合(Entry類型的集合),最後遍歷這個集合逐個解析配置類。
---------------分割線,自動配置分析------------------
看看AutoConfigurationImportSelector類如何實現DeferredImportSelector接口的getImportGroup方法
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
直接返回AutoConfigurationGroup類
上面說過,會先調用Group類型的process方法,再調用其selectImports方法,來看AutoConfigurationGroup類對這兩個方法的實現
public void process(AnnotationMetadata annotationMetadata,
DeferredImportSelector deferredImportSelector) {
...省略assert,限制deferredImportSelector的實際類型是AutoConfigurationImportSelector
//拿到META-INF/spring.factories中的EnableAutoConfiguration,並做排除、過濾處理
//AutoConfigurationEntry裏有需要引入配置類和排除掉的配置類,最終只要返回需要配置的配置類
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
annotationMetadata);
//加入緩存,List<AutoConfigurationEntry>類型
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
//加入緩存,Map<String, AnnotationMetadata>類型
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
該方法拿到配置文件META-INF/spring.factories中的EnableAutoConfiguration並做排除、過濾處理,然後緩存到成員變量中。
public Iterable<Entry> selectImports() {
//根據緩存的成員變量判斷是不是空
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
//拿到所有排除類
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream).collect(Collectors.toSet());
//拿到需要配置的類
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
//這裏移除排除的類(這裏感覺多此一舉啊)
processedConfigurations.removeAll(allExclusions);
//對配置類排序(根據註解AutoConfigureOrder、AutoConfigureBefore、AutoConfigureAfter),
//最後封裝成Entry裝入集合返回
return sortAutoConfigurations(processedConfigurations,
getAutoConfigurationMetadata())
.stream()
.map((importClassName) -> new Entry(
this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
這個方法會最終會把需要配置的類封裝成Entry,裝入集合最後返回出去,交由Spring解析處理。
總結:
SpringBoot自動配置實現是通過實現Spring提供的DeferredImportSelector接口來引入配置文件中配置的EnableAutoConfiguration,而配置文件是META-INF/spring.factories。