学一学Spring中的@Import注解

556 阅读6分钟

Spring版本: 5.3.27

JDK版本: 1.8

一、@Import在何处处理

 // ConfigurationClassParser
 ​
 /**
  * 通过从源类(理解为配置类)中读取注解、成员和方法,获取到完成的配置类
  * 由于一个配置类可能关联其他的配置类等,所以这个方法可能会被调用多次
  */
 protected final SourceClass doProcessConfigurationClass(
         ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
         throws IOException {
 ​
     if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
         // 先递归处理成员类
         processMemberClasses(configClass, sourceClass, filter);
     }
 ​
     // 处理@PropertySource注解
     for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
             sourceClass.getMetadata(), PropertySources.class,
             org.springframework.context.annotation.PropertySource.class)) {
         if (this.environment instanceof ConfigurableEnvironment) {
             processPropertySource(propertySource);
         }
         else {
             logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                     "]. Reason: Environment must implement ConfigurableEnvironment");
         }
     }
 ​
     // 处理@ComponentScan注解
     Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
             sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
     if (!componentScans.isEmpty() &&
             !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
         for (AnnotationAttributes componentScan : componentScans) {
             // 配置类有@ComponentScan注解 -> 立即执行扫描
             Set<BeanDefinitionHolder> scannedBeanDefinitions =
                     this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
             // 如果找到其他的配置类,在需要的情况下递归解析
             for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                 if (bdCand == null) {
                     bdCand = holder.getBeanDefinition();
                 }
                 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                     parse(bdCand.getBeanClassName(), holder.getBeanName());
                 }
             }
         }
     }
 ​
     // 处理@Import注解  本文要探讨的注解
     processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
 ​
     // 处理@ImportResource注解
     AnnotationAttributes importResource =
             AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
     if (importResource != null) {
         String[] resources = importResource.getStringArray("locations");
         Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
         for (String resource : resources) {
             String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
             configClass.addImportedResource(resolvedResource, readerClass);
         }
     }
 ​
     // 处理@Bean方法
     Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
     for (MethodMetadata methodMetadata : beanMethods) {
         configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
     }
 ​
     // 处理接口上的默认方法
     processInterfaces(configClass, sourceClass);
 ​
     // 超类处理
     if (sourceClass.getMetadata().hasSuperClass()) {
         String superclass = sourceClass.getMetadata().getSuperClassName();
         if (superclass != null && !superclass.startsWith("java") &&
                 !this.knownSuperclasses.containsKey(superclass)) {
             this.knownSuperclasses.put(superclass, configClass);
             // 发现超类, 返回元注解并递归
             return sourceClass.getSuperClass();
         }
     }
 ​
     // 没有超类 -> 处理完成
     return null;
 }
 ​
 // 获取通过@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;
 }
 ​
 /**
  * 递归收集通过@Import导入的类
  * 检查每个源类上的注解,如果不是@Import,把这个注解当成源类,继续收集
  * 最终的结果就是获取到所有源类上所有层次@Import上的value
  */
 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"));
     }
 }
 ​
 /**
  * importCandidates是上一步收集到的所有@Import注解的value
  */
 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
         Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
         boolean checkForCircularImports) {
 ​
     // 省略部分代码...
     
     // 遍历处理所有@Import注解的value
     for (SourceClass candidate : importCandidates) {
         if (candidate.isAssignable(ImportSelector.class)) {
             // 候选类是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);
             }
             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)) {
             // 候选类是ImportBeanDefinitionRegistrar ->
             // 委托给ImportBeanDefinitionRegistrar注册额外的bean定义信息
             Class<?> candidateClass = candidate.loadClass();
             ImportBeanDefinitionRegistrar registrar =
                     ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                             this.environment, this.resourceLoader, this.registry);
             configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
         }
         else {
             // 候选类不是ImportSelector或ImportBeanDefinitionRegistrar ->
             // 当成配置类处理(@Configuration注解的类)
             this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
             processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
         }
     }
 }

二、@Import引入的三种类型

1、引入普通组件

 package com.lazy.snail;
 ​
 /**
  * @ClassName NormalComponent
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/10/22 9:35
  * @Version 1.0
  */
 public class NormalComponent {
 }
 package com.lazy.snail;
 ​
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 ​
 @Configuration
 @Import({NormalComponent.class})
 public class AppConfig {
 ​
 }

image-20241022103210745

2、引入ImportSelector

 package com.lazy.snail;
 ​
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.EnableAsync;
 ​
 @Configuration
 @EnableAsync
 public class AppConfig {
 ​
 }
 // EnableAsync
 ​
 // 省略部分代码...
 ​
 /**
  * 启用Spring的异步方法执行能力
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 // AsyncConfigurationSelector会被选择出来进行处理
 @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;
 ​
 }
 // ConfigurationClassParser.processImports截取
 ​
 if (candidate.isAssignable(ImportSelector.class)) {
     // Candidate class is an ImportSelector -> delegate to it to determine imports
     Class<?> candidateClass = candidate.loadClass();
     // 实例化了AsyncConfigurationSelector
     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);
     }
     // 这种类型后续SpringBoot自动装配再聊
     if (selector instanceof DeferredImportSelector) {
         this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
     } else {
         // 此处的selectImports调用的是AsyncConfigurationSelector父类AdviceModeImportSelector的selectImports
         // 各种选择逻辑后返回org.springframework.scheduling.annotation.ProxyAsyncConfiguration
         String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
         // 根据全限定名找源Class
         Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
         // 把上面找到的源类当做@Import引入的类,从走取经路
         // 就本案例而言,ProxyAsyncConfiguration是普通组件,只有一个@Bean方法,所以会在容器中注册一个AsyncAnnotationBeanPostProcessor
         processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
     }
 }
 ​
 // AdviceModeImportSelector
 public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
     // 省略部分代码...
     
     // AsyncConfigurationSelector的selectImports方法
     String[] imports = selectImports(adviceMode);
     if (imports == null) {
         throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
     }
     return imports;
 }
 ​
 // AsyncConfigurationSelector
 /**
  * 这个方法返回了一个类的全限定名
  * org.springframework.scheduling.annotation.ProxyAsyncConfiguration
  */
 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;
     }
 }

AsyncConfigurationSelector类结构图:

AdviceModeImportSelector

解析结束后,容器中的beanDefinitionMap:

image-20241022111017349

3、引入ImportBeanDefinitionRegistrar

 package com.lazy.snail;
 ​
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 ​
 @Configuration
 @EnableAspectJAutoProxy
 public class AppConfig {
 ​
 }
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 // 此处导入了ImportBeanDefinitionRegistrar类型的注册器
 @Import(AspectJAutoProxyRegistrar.class)
 public @interface EnableAspectJAutoProxy {
 ​
     boolean proxyTargetClass() default false;
 ​
     boolean exposeProxy() default false;
 ​
 }
 // ConfigurationClassParser.processImports
 ​
 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
     Class<?> candidateClass = candidate.loadClass();
     // AspectJAutoProxyRegistrar实例
     ImportBeanDefinitionRegistrar registrar =
             ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                     this.environment, this.resourceLoader, this.registry);
     // 注册器加到了配置类importBeanDefinitionRegistrars中,这个集合中的注册器会在加载bean定义信息的时候使用
     configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
 }
 ​
 // ConfigurationClassPostProcessor.processConfigBeanDefinitions
 ​
 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
     // 省略部分代码...
     this.reader.loadBeanDefinitions(configClasses);
     // 省略部分代码...
 }
 ​
 // ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass
 private void loadBeanDefinitionsForConfigurationClass(
         ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
 ​
     // 省略部分代码...
     // 此处处理之前添加的注册器
     loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
 }
 ​
 /**
  * 遍历注册器,调用注册器的registerBeanDefinitions方法
  */
 private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
     registrars.forEach((registrar, metadata) ->
             registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
 }
 // AspectJAutoProxyRegistrar
 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
 ​
     @Override
     public void registerBeanDefinitions(
             AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
         // 注册AspectJAnnotationAutoProxyCreator的bean定义信息
         AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
 ​
         AnnotationAttributes enableAspectJAutoProxy =
                 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
         if (enableAspectJAutoProxy != null) {
             if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
             }
             if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
             }
         }
     }
 ​
 }

注册器处理完后,容器中的beanDefinitionMap中:

image-20241022120055570

三、总结

1. 导入普通组件

使用方式:
 @Import(SomeConfig.class)
处理机制:
  • @Import 注解的 value 中传入的是普通的组件类(包括普通的 Java 类和标记了 @Configuration 的类),Spring 容器会将这些类作为Bean直接注册到容器中。

  • 解析顺序

    • Spring 会将这些类当作标准的配置类进行处理(如果带有 @Configuration)。
    • 如果是普通类(没有 @Configuration),也会作为普通的 Bean 定义注册进容器。
使用场景:
  • 简单场景下,直接将某些普通组件或配置类引入容器进行自动装配和依赖注入。

2. 导入实现 ImportSelector 接口的类

使用方式:
 @Import(MyImportSelector.class)
处理机制:
  • ImportSelector 是一个接口,其中最核心的方法是 selectImports,它返回一个字符串数组,表示要导入的类名。

  • 解析顺序

    • Spring 会调用 selectImports 方法,获取需要导入的 Bean 名称列表(这些类并不需要提前在 @Import 注解中明确声明)。
    • Spring 根据返回的类名,动态地将这些类的定义加载到容器中。
    • 可以通过条件、配置、逻辑判断来选择哪些类需要被导入,实现更灵活的组件注册。
使用场景:
  • 需要基于逻辑条件动态引入配置类或组件,比如在某些场景下,按需引入特定 Bean。
  • 常用于自动配置机制,例如 Spring Boot 的 @EnableAutoConfiguration,其背后通过 ImportSelector 实现了动态组件引入。

3. 导入实现 ImportBeanDefinitionRegistrar 接口的类

使用方式:
 @Import(MyImportBeanDefinitionRegistrar.class)
处理机制:
  • ImportBeanDefinitionRegistrar 提供更底层的控制,它允许在 Spring Bean 定义的注册阶段,手动向容器中注册 Bean 定义信息。

  • 解析顺序

    • Spring 调用 registerBeanDefinitions 方法,该方法允许开发者直接向 BeanDefinitionRegistry 中注册 Bean 定义,而不仅仅是类名。
    • 在这个方法里,开发者可以完全自定义 Bean 的定义、依赖关系、属性等。
    • 这是最强大的扩展方式,因为它可以直接操作 Bean 定义元数据,而不仅仅是控制导入哪些类。
使用场景:
  • 需要完全自定义 Bean 注册过程,甚至可以动态生成 BeanDefinition
  • 通常用于更复杂的场景,如框架级别的开发,动态注册特定类型的 Bean 或者与其他框架集成。

三者对比总结

形式处理方式使用场景
普通组件(类)直接将类作为 Bean 注册到容器中引入静态组件、配置类,或使用简单的 @Configuration
实现 ImportSelector 接口通过返回类名字符串数组,动态选择和注册类按条件动态选择要注册的 Bean,用于自动配置等场景
实现 ImportBeanDefinitionRegistrar 接口手动操作 BeanDefinitionRegistry 注册 Bean 定义完全自定义 Bean 的注册过程,实现更复杂的动态注册逻辑