文章目录
- 基本用法
- 源码分析
- 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 {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[0];}
}
然后@Import中指定ImportSelector的实现类的Class
- ImportBeanDefinitionRegistrar方式
实现ImportBeanDefinitionRegistrar接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic 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) {// 拿到所有的BeanDefinitionfor (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或者MetadataReaderprivate 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);// 递归调用processImportsprocessImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
ImportSelector 接口
public interface ImportSelector {/*** 根据配置类的元数据信息,返回类的全限定名称* importingClassMetadata:@Import注解修饰的那个类**/String[] selectImports(AnnotationMetadata importingClassMetadata);@Nullabledefault 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的配置文件中也可以定义BeanloadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 加载ImportBeanDefinitionRegistrar中需要注册的BeanloadBeanDefinitionsFromRegistrars(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 {@Beanpublic A a() {return new A();}
}