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。