文章目录
- 基本用法
- 源码分析
- ConfigurationClassPostProcessor
- ConfigurationClass
- SourceClass
- getImports
- processImports
- 处理 ImportSelector
- ImportSelector 接口
- DeferredImportSelector
- 处理 ImportBeanDefinitionRegistrar
- ImportBeanDefinitionRegistrar 接口
- 处理Configuration
本文源码基于spring-context-5.3.36 版本
基本用法
- 直接填class数组方式
@Import注解填入要导入的类的Class即可
@Import({ A.class , B.class... })
@Configuration
public class TestDemo {
}
- ImportSelector方式
实现ImportSelector接口,selectImport返回需要导入的类的全限定名称
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[0];
}
}
然后@Import中指定ImportSelector的实现类的Class
- ImportBeanDefinitionRegistrar方式
实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 通过 BeanDefinitionRegistry 构造 BeanDefinition 并进行注册
}
}
然后@Import中指定ImportBeanDefinitionRegistrar的实现类的Class
总结
- 以上三种用法方式皆可混合在一个@Import中使用,注意第一种和第二种都是以全类名的方式注册,而第三中可自定义名称
- 第一种最简单,第二种较简单,第三种需要操作BeanDefinition,较为复杂
- @Import注解不一定非要和@Configuration搭配使用,也可以和@Component等注解使用,效果一样
@Import({ A.class , B.class... })
@Component
public class TestDemo {
}
源码分析
入口
BeanDefinitionRegistryPostProcessor执行阶段生效
例如 ConfigurationClassPostProcessor
ConfigurationClassPostProcessor
ConfigurationClass
代表定义的@Configuration修饰的类,包含一些bean方法。配置加载都有一个解析过程,对ConfigurationClass的解析就是由ConfigurationClassParser#parse
方法完成的,它会解析每个配置类上的配置,包括@Import注解这个配置
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 拿到所有的BeanDefinition
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
...
}
}
this.deferredImportSelectorHandler.process();
}
SourceClass
org.springframework.context.annotation.ConfigurationClassParser.SourceClass
主要用于处理和解析配置类的源信息
private class SourceClass implements Ordered {
// Class或者MetadataReader
private final Object source;
// 注解元数据
private final AnnotationMetadata metadata;
}
主要作用如下:
- 表示源类:SourceClass 代表 Spring 配置类中的一个类,它封装了获取该类相关的源信息的方法,比如类的名称、注解、方法等。
- 提供元信息:SourceClass 提供了一些元数据的方法,帮助开发者获取类的详细信息,比如父类、实现的接口、注解信息等。
getImports
SourceClass可以简单的理解为java里的Class,同样,SourceClass可以代表普通类,也可以代表注解。同时SourceClass携带有一些注解元数据信息。其实getImports方法的过程就和根据反射获取一个类上的所有注解(包括修饰注解的注解)这个过程差不多
使用深度优先遍历
- 首先定义一个visited记录已经访问过的 SourceClass
- 对于每个访问的 SourceClass ,如果它被
@Import
注解修饰,则获取@Import
注解的属性值
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
调用的collectImports()
方法是一个递归操作,从第一个SourceClass开始,获取其所有注解,然后调用collectImports递归进行收集。因为@Import可能放在注解上形成复合注解
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
最后返回的imports就是从根SourceClass开始遍历的所有@Import注解的值,这个值是一个java中Class的集合
processImports
getImports的返回值作为processImports方法的第三个参数
/**
*
* @param configClass 配置类,一般是@Configuration修饰的类
* @param currentSourceClass 扫描@Import注解的根节点
* @param importCandidates currentSourceClass上携带的所有@Import注解的属性值
* @param exclusionFilter 用于排除
* @param checkForCircularImports 是否检查@Import形成环的情况
*/
private void processImports(ConfigurationClass configClass,
SourceClass currentSourceClass,
Collection<SourceClass> importCandidates,
Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
// 检测可能存在的循环@Import情况
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(
new CircularImportProblem(configClass, this.importStack));
} else {
// 下面代码保留了整体结构,省略了不重要的细节
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 一些if/else判断
if (candidate.isAssignable(ImportSelector.class)) {
...
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
...
} else {
// 既不是ImportSelector也不是ImportBeanDefinitionRegistrar
// 当作@Configuration类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 继续走处理配置类的流程,会继续进行processImports方法
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
} catch (BeanDefinitionStoreException e) {
// 省略
}
finally {
this.importStack.pop();
}
}
}
处理 ImportSelector
如果是ImportSelector
处理细节如下所示
// 实例化ImportSelector对象
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);
}
// 区分是否是DeferredImportSelector
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 所有导入的类名称
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 将ImportSelector返回的类名转换为SourceClass
// 使用Class.forName转换成Class实例,构造SourceClass对象
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归调用processImports
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
ImportSelector 接口
public interface ImportSelector {
/**
* 根据配置类的元数据信息,返回类的全限定名称
* importingClassMetadata:@Import注解修饰的那个类
**/
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
DeferredImportSelector
Deferred意思是延迟,相比于ImportSelector会立即被处理,DeferredImportSelector的selectImport方法会在当前配置类所有的Bean信息解析完毕后才进行处理
DeferredImportSelector的作用是用于调整Import的Bean和当前配置类配置的Bean的先后关系
因为有条件注解的存在,所以需要Bean之间的注册有先后关系,条件注解才能发挥作用
处理 ImportBeanDefinitionRegistrar
如果SourceClass类型是ImportBeanDefinitionRegistrar的实现类简单,则调用org.springframework.context.annotation.ConfigurationClass#addImportBeanDefinitionRegistrar
方法
// 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());
ImportBeanDefinitionRegistrar 接口
ImportBeanDefinitionRegistrar接口用于手动注册BeanDefinition
public interface ImportBeanDefinitionRegistrar {
/**
* 注册BeanDefinition
* BeanDefinitionRegistryPostProcessor此时还未注册
* @param importingClassMetadata 导入的类的注解元数据
* @param registry 当前BeanDefinitionRegistry,即BeanFactory实现
* @param importBeanNameGenerator 导入的Bean的命名策略
*/
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
- BeanNameGenerator:默认实现是
ConfigurationClassPostProcessor#IMPORT_BEAN_NAME_GENERATOR
或者通过ConfigurationClassPostProcessor#setBeanNameGenerator
进行设置 - BeanDefinitionRegistry registry:其实就是BeanFactory
@Configuration配置类解析完毕后,下一步就是将让配置生效。如下图所示,通过ConfigurationClassBeanDefinitionReader来加载BeanDefinition
ConfigurationClassBeanDefinitionReader加载BeanDefinition的过程中条件注解会生效
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
private void loadBeanDefinitionsForConfigurationClass(
ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
String beanName = configClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}
// 处理已经导入的配置类
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 配置类的所有@Bean修饰的方法
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
// @ImportedResources用于导入Spring的配置文件
// 而Spring的配置文件中也可以定义Bean
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 加载ImportBeanDefinitionRegistrar中需要注册的Bean
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
处理Configuration
如果@Import的内容既不是ImportSelector也不是ImportBeanDefinitionRegistrar,那么就把它当作@Configuration修饰的类进行处理,无论它是不是真的被@Configuration修饰
// 既不是ImportSelector也不是ImportBeanDefinitionRegistrar
// 当作@Configuration类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 继续走处理配置类的流程,会继续进行processImports方法
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
假如@Import的是一个普通类,比如下面这样,什么都没有。由于@Import的存在,SimpleClass还是会被当作Bean注册进容器
public class SimpleClass {
}
如果给它加上@Configuration配置类可以有的配置,它也是会生效的
public class SimpleClass {
@Bean
public A a() {
return new A();
}
}