springboot自动装配

前言

springboot 大幅简化了 spring 框架的繁琐配置,非常容易快速上手。一个简单的 springboot 应用,只要加上@SpringBootApplication 注解就可以实现自动配置。而对于许多第三方库,只要引入相应的 starter,就可以直接生成相应的 bean 而无需自行初始化。而这就是 springboot 的自动装配机制。

那么自动装配机制是如何实现的呢?今天我们就来探究一下。

导航

@EnableAutoConfiguration 注解

@SpringBootApplication 注解集合了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 三大注解,自动装配无疑和@EnableAutoConfiguration 注解有关。

1
2
3
4
5
6
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

可以看到@EnableAutoConfiguration 注解使用@Import 导入了两个类,分别是 AutoConfigurationPackages.Registrar 和 AutoConfigurationImportSelector。

先来看@Import 注解。调用关系是这样的:

1
2
3
4
->context.refresh()
->context.invokeBeanFactoryPostProcessors()
->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
->PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(postProcessors,...)

这里的 postProcessors 包括 ConfigurationClassPostProcessor,将会调用其的 postProcessBeanDefinitionRegistry()方法,这个方法会加载注册更多的 bean 定义信息, 主要是通过 ConfigurationClassParser 的 parse()方法和 ConfigurationClassBeanDefinitionReader 的 loadBeanDefinitions()。

其中各种注解的解析在 parse()中,包括@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean 等。注意,@import 注解就在其中:

1
2
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

由此可见,processImports()方法就是@Import 的处理逻辑,点开它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// ...
for (SourceClass candidate : importCandidates) {
// 实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// ...
// 如果是DeferredImportSelector接口类型
if (selector instanceof DeferredImportSelector) {
// 与ImportSelector相比,实现该子接口的类将会在所有配置类之后执行,并且可以用@Order排序
// AutoConfigurationImportSelector走到这里
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else { // 否则便执行selectImports()方法
// 注意AutoConfigurationImportSelector并不会走到这里
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 实现了ImportBeanDefinitionRegistrar接口,动态生成BeanDefinition对象并注册
// AutoConfigurationPackages.Registrar类实现了此接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else { // 当做普通@Configuration类处理
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}

由于 AutoConfigurationImportSelector 继承的是 ImportSelector 的子接口 DeferredImportSelector,所以在这里它并不会直接调用 selectImports 方法,而是通过 handler 获取其静态内部类 AutoConfigurationGroup,执行内部类的 process()和 selectImports()方法,其中 process()就会扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 里的 class,注意自 3.0 版本的路径不再是 META-INF/spring.factories 了。

@Conditional

打开 starter,经常发现配置类上有各种 Conditional 注解,如@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnProperty 等等,那么这些注解是怎么起作用的呢?追踪源码发现,依然是在 ConfigurationClassParser 的 doProcessConfigurationClass()方法中:

1
2
3
4
if (!componentScans.isEmpty() &&
// conditionEvaluator用于处理 @Conditional 注解
// 最终会根据Condition接口下的matches() boolean 方法判断
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {}