Spring Framework自3.1开始,支持“@Enable 模块驱动”。其意义在于能够简化装配步骤,实现“按需装配”,同时屏蔽组件集合装配的细节。Spring Framework在实现上分为两类:“注解驱动”和“接口编程”。两种实现均需要依赖@Import,其职责是导入一个或多个组件类( component classes ),将其定义为Spring Bean。
这里的组件类分两种:

  • 被标注@Configuration的类;普通的组件类,作用类似AnnotationConfigApplicationContext#register(对应注解驱动)
  • ImportSelector或者ImportBeanDefinitionRegistrar接口的实现类(对应接口编程)。

基于”注解驱动”

@EnableWebMvc注解是基于“注解驱动”的例子:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
1
2
3
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}

基于“接口编程”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Interface to be implemented by types that determine which @{@link Configuration}
* class(es) should be imported based on a given selection criteria, usually one or
* more annotation attributes.
*/
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);

@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}

}

ImportSelector接口的作用是判断哪些被@Configuration标注的类应该被导入,判断条件一般是一个或多个注解属性。ImportSelector接口定义了一个selectImports方法,返回需要被导入的类的全称限定名。入参是正在被导入的@Configuration类的AnnotationMetadata。这里正在被导入的@Configuration类是指@Import标注的类,在“**@Enable 模块驱动”中指被@Enable**标注的类。

1
2
3
4
5
6
7
8
9
10
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}

default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}

}

ImportBeanDefinitionRegistrar接口相对于ImportSelector而言,多了一个BeanDefinitionRegistry,作为入参,将Bean定义(BeanDefinition)的注册也交给了开发人员。

@Enable模块驱动原理

​ 在Spring应用上下文启动过程中(AbstractApplicationContext#refresh()方法被调用时),Spring容器(BeanFactory)将ConfigurationClassPostProcessor初始化为Spring Bean,作为优先级最高的BeanFactoryPostProcessor实现,随后其postProcessBeanFactory方法被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}

enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

列一下processConfigBeanDefinitions主要的步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
...
// 将configuration类或者在configuration/component类中声明的嵌套component类加入configCandidates
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
...
// 使用ConfigurationClassParser解析每一个@Configuration类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
...
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
...
parser.parse(candidates);
...
}

parse方法将@Configuration类包装为ConfigurationClass,然后调用processConfigurationClass方法。

1
2
3
4
5
6
7
8
9
10
11
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
...
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);
}

processConfigurationClass方法又调用了doProcessConfigurationClass进行处理。doProcessConfigurationClass方法会处理很多注解,例如@Component@PropertySource@ComponentScan@Bean方法等。我们这里只关注对@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
32
33
34
35
36
37
38
39
40
41
42
43
44
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
...
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}

可以看到processImports方法会处理ImportSelector实现、ImportBeanDefinitionRegistrar实现,其他情况会视为@Configuration类,再调用回processConfigurationClass方法。最后ConfigurationClass集合将被注册为Spring Bean。

ConfigurationClassPostProcessor不仅检查@Configuration和@Bean两种Bean定义方式,还处理@Component@Configuration类标注为“完全模式”,而@Component和@Bean方法则是“轻量模式”。最后使用CGLib实现ConfigurationClassEnhancer对@Configuration类进行提升。

综上所述,ConfigurationClassPostProcessor负责筛选@Component类、@Configuration类及@Bean方法的BeanDefinition,ConfigurationClassParser则从候选的Bean定义中解析出ConfigurationClass集合并注册成Spring Bean。