@Import 注解给 Spring bean 创建带来很大的灵活性,因其对配置的封装,极大简化了 Spring 的使用。Spring 中的 @Enable* 基本上都是通过 @Import 来实现的 (在 @Enable 上添加 @Import 注解来导入其他配置类啥的)
@Import 的使用方式
通常在配置类上使用 @Import 导入其他配置类
- 有 @Configuration注解, 标记为 FULL 配置类
- 没有 @Configuration 注解, 但是存在 @Component, @ComponentScan, @Import, @ImportResource, 或者类中有 @Bean 注解标注的方法, 标记为 LITE 配置类
@Import 的三种使用方式
- 直接导入一个配置类 (4.2后也可以导入普通类)
- 导入 ImportSelector 接口的实现类, 解析 selectImports 方法返回的一组类为 BeanDefinition 并注册
- 导入 ImportBeanDefinitionRegistrar 接口的实现类, 自行构建并注册 BeanDefinition
哪些 @Import 能被解析
- 配置类上的 @Import
- 配置类上的其他注解内部的 @Import, 以及从配置类起, 递归非 @Import 注解内的 @Import
如果被扫描的 @Import 导入了一个非配置类, 但该类上有 @Import, 则也会被解析到
注意, 被 @Import 导入的类不会被注册成为 BeanDefinition, 不会生产单例 Bean
导入配置类
@EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {}
ImportSelector
@EnableAsync
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case ASPECTJ:
return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
}
ImportSelector 的 selectImports 方法的主要作用是收集需要导入的配置类, 方法返回的一组类限定名会被当做配置类来解析(走解析配置类的完整流程, 会解析这个类上的 @PropertySource, @ComponentScan, @Import, @ImportSource, @Bean 方法 等), ImportSelector 本身并不会被注册成为 BeanDefinition
在调用该 ImportSelector 的 selectImports 方法之前, 如果该导入的 ImportSelector 还实现 EnvironmentAware, BeanFactoryAware, BeanClassLoaderAware, ResourceLoaderAware, 那么会将这些拿到并 set 到该 ImportSelector 的对应属性中
DeferredImportSelector 延迟导入
@EnableAutoConfiguration
@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 {}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
// ...
public Class<? extends Group> getImportGroup() {
return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
}
// ...
}
DeferredImportSelector 接口是 ImportSelector 接口的子接口,该接口引入的配置类将在Spring解析完其他注解和配置类(包括但不限于ImportSelector引入的配置类, 不包括自动化配置类即spring.factories文件中的配置类)后再解析, 依赖此设计可以实现SpringBoot的自动配置, 自动化配置类在被解析时会根据条件判断(@Conditional)是否Spring容器中已经存在用户配置的Bean, 存在的话就不会执行自动化配置了. 此接口还可以和接口Ordered或者@Order一起使用,定义多个DeferredImportSelector选择器的优先级
还提供分组功能, 先调用DeferredImportSelector的getImportGroup方法判断是否有自定义组, 有的话就调该组的selectImports方法, 没有的话就调DeferredImportSelector的selectImports方法. 不同的ImportSelector导入的Bean可以被分组, 在组的selectImports方法中可以排序
ImportBeanDefinitionRegistrar
@EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
ImportBeanDefinitionRegistrar 接口提供了 registerBeanDefinitions 方法的两个默认实现, 可以覆盖该方法, 自定义 BeanDefinition 并注册
解析 @Import 的流程
- 从当前配置类起, 递归查找类上的 @Import 注解, 以及类上非 @Import 注解(注解类名以java开头的过滤掉)里面的 @Import 注解. 拿到所有 @Import 注解导入的类. 比如当前解析的是配置类, 配置类 上 @Import(A.class), A 上 @Import(B.class), 配置类 上还有一个 @Xxxxx 注解, 这个注解里 @Import(C.class), 最终拿到的是 A.class, C.class
- 遍历拿到的被导入的类
- 如果被导入的类 是 ImportSelector (导入型选择器: 用于选择需要导入的类)
- 如果被导入的类 是延迟导入 DeferredImportSelector (延迟导入型选择器: 用于选择需要导入的类, 被导入的类的解析会延迟), 会把该导入型选择器存在 ConfigurationClassParser 的 deferredImportSelectors 中. 后面哪里处理
- 如果被导入的类 不是延迟导入, 递归导入该类导入的类再导入的类. 递归解析的最后一定会到 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 的类
- 如果被导入的类 是 ImportBeanDefinitionRegistrar (导入型 BeanDefinition 注册器: 用于注册需要导入的 BeanDefinition), 将这个 ImportBeanDefinitionRegistrar 添加到 configClass 的 importBeanDefinitionRegistrars 中. 在 parse 后面的 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 流程中被遍历调用其方法来注册 BeanDefinition
- 如果被导入的类 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar, 将其作为一个配置类来解析, 走解析 @Configuration 相同的流程. 并没有直接生成 BeanDefinition 并注册, 而是先保存在 ConfigurationClassParser 的 importStack 的 import 中
- 如果被导入的类 是 ImportSelector (导入型选择器: 用于选择需要导入的类)
详细流程说明
ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
// ...
// 开始解析配置类
// 逐个解析配置类的 @PropertySource, @ComponentScan, @Import, @ImportResource, @Bean 方法
// @ComponentScan 扫描 basePackages, 扫描到的组件会注册成为 BeanDefinition, 如果是配置类则递归解析
// @Import 导入的类会分成3种,
// ImportSelector 中延迟的被暂存起来, 非延迟的被递归解析,
// ImportBeanDefinitionRegistrar 的被暂存起来,
// 都不是的会被当做配置类走配置类解析流程
// @ImportResource 导入的 xml 也被暂存起来
// @Bean 注解的方法也被暂存起来
parser.parse(candidates);
// 将上述暂存起来的都统一处理
// 取到 parser 里面解析过的配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 用一个 reader 从被解析过的配置类中读取并注册 BeanDefinition
// ImportBeanDefinitionRegistrar 的后续注册 BeanDefinition 就在这里
// DeferredImportSelector 的后续处理 还没有找到
this.reader.loadBeanDefinitions(configClasses);
// ...
}
ConfigurationClassParser#doProcessConfigurationClass 解析 @Import 部分
// Process any @Import annotations
// 处理 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
ConfigurationClassParser#getImports
从当前配置类起, 递归查找类上的 @Import 注解, 以及类上非 @Import 注解(注解类名以java开头的过滤掉)里面的 @Import 注解. 拿到所有 @Import 注解导入的类. 比如当前解析的是配置类, 配置类 上 @Import(A.class), A 上 @Import(B.class), 配置类 上还有一个 @Xxxxx 注解, 这个注解里 @Import(C.class), 最终拿到的是 A.class, C.class
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
// 拿到 sourceClass 上的 @Import 注解的 导入类,
// 以及 sourceClass 上的其他注解内引入的 @Import 注解的 导入类,
// 以及 sourceClass 上的其他注解内的其他注解内引入的 @Import 注解的 导入类
// 以及 sourceClass 上的其他注解内的其他注解内的其他注解内引入的 @Import 注解的 导入类
// ......
// 递归层层拿
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"));
}
}
ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 被导入的 是 ImportSelector (导入型选择器: 用于选择需要导入的类)
// 判断的其实是 候选组件类 是否是 ImportSelector 或 候选组件类 是否是 ImportSelector 的父类
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 将这个 ImportSelector 实例化, 如有必要则注入4个 Aware
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 (延迟导入型选择器: 用于选择需要导入的类, 被导入的类的解析会延迟)
// 会把该导入型选择器存在 ConfigurationClassParser 的 deferredImportSelectors 中
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
// 不是延迟导入
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归导入该类导入的类再导入的类
// 递归解析的最后一定是 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 的类
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 被导入的 是 ImportBeanDefinitionRegistrar (导入型 BeanDefinition 注册器: 用于注册需要导入的 BeanDefinition)
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
// 将这个 ImportBeanDefinitionRegistrar 实例化, 如有必要则注入4个 Aware
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 将这个 ImportBeanDefinitionRegistrar 添加到 configClass 的 importBeanDefinitionRegistrars 中
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 被导入的 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar
// 将其作为一个配置类来解析, 走解析 @Configuration 相同的流程
// 并没有直接生成 BeanDefinition 并注册, 而是先保存在 ConfigurationClassParser 的 importStack 的 import 中
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
System.out.println(this.importStack.imports);
// 将被导入的类的信息添加到 ConfigurationClassParser 的 importStack 的 import 中
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
System.out.println(this.importStack.imports);
// 将被导入的类当成一个配置类来解析, 走解析 @Configuration 相同的流程
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}