写点什么

spring4.1.8 扩展实战之八:Import 注解

作者:程序员欣宸
  • 2022 年 6 月 19 日
  • 本文字数:12505 字

    阅读完需:约 41 分钟

spring4.1.8扩展实战之八:Import注解

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos


  • 在 spring 框架下做开发时,@Import 是常见的注解,可以用来动态创建 bean,今天我们先从源码分析原理,再用实战来验证 Import 的作用;

文章概览

  • 本章由以下几部分组成:


  1. 从 Enable 前缀的注解谈起,揭示常见的 Enable 注解与 Import 注解的关系;

  2. 常见的四种 Import 注解用法列举;

  3. 分析 spring 源码,揭示 Import 注解的工作原理;

  4. 官方 API 文档中的疑问解答;

  5. 实战通过 Import 注解动态创建 bean 实例;

从 Enable 前缀的注解谈起

  • 有很多注解都以 Enable 为前缀,例如配置异步调用的注解 EnableAsync,其源码如下 :


@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {  Class<? extends Annotation> annotation() default Annotation.class;    AdviceMode mode() default AdviceMode.PROXY;    int order() default Ordered.LOWEST_PRECEDENCE;}
复制代码


  • 从以上代码可见,使异步调用生效的关键是**@Import(AsyncConfigurationSelector.class)**,通过此注解 spring 容器会创建 AsyncConfigurationSelector 实例并调用其 selectImports 方法,完成异步调用相关的配置;

  • 再多看几个 Enable 前缀的注解的源码,例如 EnableBatchProcessing、EnableCaching、EnableDiscoveryClient 等,也都是通过 Import 来生效的,这种方式值得我们学习,在业务开发中也能用类似方式来对 bean 实例做控制;

常见的四种 Import 注解用法列举

  • 在 @Import 注解的参数中可以填写类名,例如**@Import(Abc.class)**,根据类 Abc 的不同类型,spring 容器有以下四种处理方式:

  • 如果 Abc 类实现了 ImportSelector 接口,spring 容器就会实例化 Abc 类,并且调用其 selectImports 方法;

  • DeferredImportSelector 是 ImportSelector 的子类,如果 Abc 类实现了 DeferredImportSelector 接口,spring 容器就会实例化 Abc 类,并且调用其 selectImports 方法,和 ImportSelector 的实例不同的是,DeferredImportSelector 的实例的 selectImports 方法调用时机晚于 ImportSelector 的实例,要等到 @Configuration 注解中相关的业务全部都处理完了才会调用(具体逻辑在 ConfigurationClassParser.processDeferredImportSelectors 方法中),想了解更多 DeferredImportSelector 和 ImportSelector 的区别,请参考《ImportSelector 与 DeferredImportSelector 的区别(spring4) 》

  • 如果 Abc 类实现了 ImportBeanDefinitionRegistrar 接口,spring 容器就会实例化 Abc 类,并且调用其 registerBeanDefinitions 方法;

  • 如果 Abc 没有实现 ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar 等其中的任何一个,spring 容器就会实例化 Abc 类,官方说明在这里

分析 spring 源码,揭示 Import 注解的工作原理

  • 接下来通过 spring 源码来了解 spring 容器是如何处理 Import 注解的;

  • 先看 spring 容器的初始化代码,定位 AbstractApplicationContext 类的 refresh 方法,里面会调用 invokeBeanFactoryPostProcessors 方法,如下图红框所示:



  • 展开 invokeBeanFactoryPostProcessors 方法,继续追踪到了 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 方法,具体操作如下图红框所示:



  • 对于上图分析的调用 invokeBeanDefinitionRegistryPostProcessors 方法时作为入参传入的 bean,ConfigurationClassPostProcessor 类的实例是符合过滤要求的:既实现了 BeanDefinitionRegistryPostProcessor 接口,又实现了 PriorityOrdered 接口,因此,在 invokeBeanDefinitionRegistryPostProcessors 方法中,ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法被调用:

  • ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法中,如下图红框所示,processConfigBeanDefinitions 方法负责处理 @Configuration 注解相关的业务:



  • processConfigBeanDefinitions 方法代码如下,请注意中文注释:


    //被确认为配置类的bean定义都放在集合configCandidates中    Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();    //取所有bean的名称    String[] candidateNames = registry.getBeanDefinitionNames();    //逐个检查每个bean    for (String beanName : candidateNames) {      //取得每个bean的定义对象      BeanDefinition beanDef = registry.getBeanDefinition(beanName);      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||          ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {        if (logger.isDebugEnabled()) {          logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);        }      }      //注意:ConfigurationClassUtils.checkConfigurationClassCandidate方法非常值得一看,里面的通过当前类的注解来判断是否为配置类      else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {        //例如有@Configuration注解的类,被判定为配置类,放入集合configCandidates中        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));      }    }
// 如果一个配置类都没找到,就直接返回了 if (configCandidates.isEmpty()) { return; }
// Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry singletonRegistry = null; if (registry instanceof SingletonBeanRegistry) { singletonRegistry = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) { BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } }
//实例化ConfigurationClassParser对象,用来处理配置类 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size()); do { //parse方法是处理配置类逻辑的核心代码 parser.parse(configCandidates); parser.validate();
复制代码


  • 看 ConfigurationClassParser 类的 parse 方法:


public void parse(Set<BeanDefinitionHolder> configCandidates) {    //稍后执行的parse方法中,所有DeferredImportSelector实现类都会被放入集合deferredImportSelectors中    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { //在这个parse方法中,所有DeferredImportSelector实现类都会被放入集合deferredImportSelectors中,它们的selectImports方法不会被执行,而其他ImportSelector实现类的selectImports都会被执行 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) { throw ex; } catch (Exception ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } //此方法内,会将集合deferredImportSelectors中的所有对象取出来执行其selectImports方法 processDeferredImportSelectors(); }
复制代码


  • 从上述代码中可以看出,DeferredImportSelector 实现类的 selectImports 方法会在最后被调用,其余的关键逻辑应该在**parse(AnnotationMetadata metadata, String beanName)**这个关键方法中,顺着这个方法一直追踪下去,直到 doProcessConfigurationClass 方法,如下图红框所示,所有 Import 注解的处理,都在 processImports 方法中:



  • processImports 方法中包含了对 ImportSelector 实现类和 ImportBeanDefinitionRegistrar 实现类的处理,以及未实现这些接口的类的处理,我们逐个来分析吧,首先看 ImportBeanDefinitionRegistrar 实现类的处理,如下图红框位置,调用 configClass.addImportBeanDefinitionRegistrar 方法将 ImportBeanDefinitionRegistrar 实现类存入 configClass 的成员变量 importBeanDefinitionRegistrars 中,后面的 ConfigurationClassPostProcessor 类的 processConfigBeanDefinitions 方法中,**this.reader.loadBeanDefinitions(configClasses);**会调用这些 ImportBeanDefinitionRegistrar 实现类的 registerBeanDefinitions 方法:



  • 再来看 processImports 方法中对 ImportSelector 实现类的处理,这里略有些复杂,因为涉及到对 processImports 方法的迭代调用,请看下图红框旁边的红字说明:



  • 如上图所示,第二步就是在 processImports 方法中调用了 processImports 方法,再次进入 processImports 之后,会着 ImportSelector 实现类返回的 bean 名称直接走到第三步的位置,第三步处理的就是没有实现 ImportSelector 和 ImportBeanDefinitionRegistrar 这些接口的普通 bean 了;

  • processImports 方法对没有实现 ImportSelector 和 ImportBeanDefinitionRegistrar 这些接口的普通 bean 的处理是执行 processConfigurationClass 方法,将这些 bean 放入了成员变量 configurationClasses 中,如下图红框所示:



  • processImports 方法分析完毕,Import 注解导入的 bean 都被保存在 ConfigurationClassParser 实例中,我们回到 ConfigurationClassPostProcessor 类的 processConfigBeanDefinitions 方法,如下图,**this.reader.loadBeanDefinitions(configClasses);**负责处理 processImports 方法找出的那些打算通过 @Import 注解来注册到 spring 容器的 bean:



  • 展开 this.reader.loadBeanDefinitions(configClasses)方法,在 ConfigurationClassBeanDefinitionReader 类中,是对每个配置类逐个执行 loadBeanDefinitionsForConfigurationClass 方法:


public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();    for (ConfigurationClass configClass : configurationModel) {      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);    }  }
复制代码


  • 展开方法,真相大白,实现了 ImportBeanDefinitionRegistrar 接口的实例,会执行其 registerBeanDefinitions 方法,其余普通的类,通过 loadBeanDefinitionsFromImportedResources 方法将其 bean 定义注册在 spring 环境:


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); } for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //普通的类,通过loadBeanDefinitionsFromImportedResources方法将其bean定义注册在spring环境 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); //实现了ImportBeanDefinitionRegistrar接口的实例,会在loadBeanDefinitionsFromRegistrars方法中执行其registerBeanDefinitions方法 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
复制代码


  • 前面将普通类、ImportBeanDefinitionRegistrar 实现类、ImportSelector 实现类的分析已经完成,对于 DeferredImportSelector 实现类的处理在 processDeferredImportSelectors 方法中,其实和 ImportSelector 实现类的处理并无区别,只是处理时机比起 ImportSelector 实现类略晚,这里就不多说了;

  • 至此,通过 Import 注解注册 bean 的四种方式已经全部分析完毕,小结如下:


  1. 普通类(即没有实现 ImportBeanDefinitionRegistrar、ImportSelector、DeferredImportSelector 等接口的类)会通过 ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromImportedResources 方法将 bean 定义注册到 spring 容器;

  2. ImportSelector 实现类,其 selectImports 方法返回的 bean 的名称,通过 ConfigurationClassParser 类的 asSourceClass 方法转成 SourceClass 对象,然后被当作普通类处理;

  3. DeferredImportSelector 实现类的处理和 ImportSelector 实现类的处理并无区别,只是处理时机比起 ImportSelector 实现类略晚;

  4. ImportBeanDefinitionRegistrar 实现类的 registerBeanDefinitions 方法会被调用,里面可以注册业务所需的 bean 定义;

官方 API 文档中的疑问解答

  • 官方API文档中,对 ImportSelector 接口的描述如下图所示,红框中的一段意思是:** ImportSelector 接口的实现类,如果同时也实现了 EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware 这些接口中的一个或几个,那么这些接口对应的方法优先执行,然后才会执行 ImportSelector 接口的 selectImports**:



上图红框中的描述会让我们不禁疑惑:spring 是如何做到的呢?一起来看源码吧:


  • 再次打开 ConfigurationClassParser 类的 processImports 方法,如下图两个红框所示,对于 @Import 注解值中的类,只要实现了 ImportBeanDefinitionRegistrar、ImportSelector、DeferredImportSelector 等接口中的任何一个,都会调用 invokeAwareMethods 方法(如果实现的是 ImportSelector 或 DeferredImportSelector 接口,此时还没有执行 selectImports 方法):

  • 展开 invokeAwareMethods 方法,真相大白,这里面检查是否实现了 EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware 等接口,如果实现了就调用对应的方法;


private void invokeAwareMethods(Object importStrategyBean) {  if (importStrategyBean instanceof Aware) {    if (importStrategyBean instanceof EnvironmentAware) {      ((EnvironmentAware) importStrategyBean).setEnvironment(this.environment);    }    if (importStrategyBean instanceof ResourceLoaderAware) {      ((ResourceLoaderAware) importStrategyBean).setResourceLoader(this.resourceLoader);    }    if (importStrategyBean instanceof BeanClassLoaderAware) {      ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ?          ((ConfigurableBeanFactory) this.registry).getBeanClassLoader() :          this.resourceLoader.getClassLoader());      ((BeanClassLoaderAware) importStrategyBean).setBeanClassLoader(classLoader);    }    if (importStrategyBean instanceof BeanFactoryAware && this.registry instanceof BeanFactory) {      ((BeanFactoryAware) importStrategyBean).setBeanFactory((BeanFactory) this.registry);    }  }}
复制代码


  • 至此,源码分析工作已经结束,接下来实战 @Import 注解的使用;

实战通过 Import 注解动态创建 bean 实例

  • 到了实战验证环节了,本次实战的内容是创建一个 springboot 工程,通过 @Import 注解将 bean 注册到 spring 容器,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章源码在文件夹 customizeimport 下,如下图红框所示:



  • 下面动手开发:

  • 设计:customizeimport 工程中,有四个接口(CustomizeService1,CustomizeService2,CustomizeService3,CustomizeService4,每个接口有个对应的实现类(CustomizeServiceImpl1、CustomizeServiceImpl2、CustomizeServiceImpl3、CustomizeServiceImpl4),在配置类通过 @Import 注解,用不同方式将这些实现类的实例注册到 spring 容器中,在一个 Controller 中验证这四个实例,实例的注册到 spring 容器方式规划如下表格所示:



  • 创建 maven 工程 customizeimport,pom.xml 内容如下:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>
<groupId>com.bolingcavalry</groupId> <artifactId>customizeimport</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging>
<name>customizeimport</name> <description>Demo project for Spring Import annotation</description>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.15.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
复制代码


  • 创建四个接口,除了类名不同,其他的都一样,如下:


package com.bolingcavalry.customizeimport.service;
public interface CustomizeService1 { void execute();}
复制代码


  • 创建接口实现类,除了类名不同,其他的都一样,如下:


package com.bolingcavalry.customizeimport.service.impl;
import com.bolingcavalry.customizeimport.service.CustomizeService1;import com.bolingcavalry.customizeimport.util.Utils;
public class CustomizeServiceImpl1 implements CustomizeService1 {
public CustomizeServiceImpl1() { Utils.printTrack("construct : " + this.getClass().getSimpleName()); }
@Override
public void execute() { System.out.println("execute : " + this.getClass().getSimpleName()); }}
复制代码


  • 创建 ImportSelector 接口的实现类 CustomizeImportSelector,其 selectImports 方法返回了 CustomizeServiceImpl2 的完整类名:


package com.bolingcavalry.customizeimport.selector;
import com.bolingcavalry.customizeimport.util.Utils;import org.springframework.context.annotation.ImportSelector;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotationMetadata;
public class CustomizeImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { Utils.printTrack("selectImports : " + this.getClass().getSimpleName()); return new String[]{"com.bolingcavalry.customizeimport.service.impl.CustomizeServiceImpl2"}; }}
复制代码


  • 创建 DeferredImportSelector 接口的实现类 CustomizeDeferredImportSelector,其 selectImports 方法返回了 CustomizeServiceImpl3 的完整类名:


package com.bolingcavalry.customizeimport.selector;
import com.bolingcavalry.customizeimport.util.Utils;import org.springframework.context.annotation.DeferredImportSelector;import org.springframework.core.annotation.Order;import org.springframework.core.type.AnnotationMetadata;
public class CustomizeDeferredImportSelector implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { Utils.printTrack("selectImports : " + this.getClass().getSimpleName()); return new String[]{"com.bolingcavalry.customizeimport.service.impl.CustomizeServiceImpl3"}; }}
复制代码


  • 创建 ImportBeanDefinitionRegistrar 接口的实现类 CustomizeImportBeanDefinitionRegistrar,其 registerBeanDefinitions 方法中在 spring 容器注册了 CustomizeServiceImpl4 类的定义:


package com.bolingcavalry.customizeimport.registrar;
import com.bolingcavalry.customizeimport.service.impl.CustomizeServiceImpl4;import com.bolingcavalry.customizeimport.util.Utils;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.GenericBeanDefinition;import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;import org.springframework.core.type.AnnotationMetadata;
public class CustomizeImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
private final static String BEAN_NAME = "customizeService4";
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BEAN_NAME)) { Utils.printTrack("start registerBeanDefinitions"); GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(CustomizeServiceImpl4.class); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(BEAN_NAME, beanDefinition); } }}
复制代码


  • 创建配置类 SysConfig,使用了 @Import 注解:


package com.bolingcavalry.customizeimport;
import com.bolingcavalry.customizeimport.registrar.CustomizeImportBeanDefinitionRegistrar;import com.bolingcavalry.customizeimport.selector.CustomizeDeferredImportSelector;import com.bolingcavalry.customizeimport.selector.CustomizeImportSelector;import com.bolingcavalry.customizeimport.service.impl.CustomizeServiceImpl1;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;
@Configuration@Import({CustomizeServiceImpl1.class, CustomizeImportSelector.class, CustomizeDeferredImportSelector.class, CustomizeImportBeanDefinitionRegistrar.class})public class SysConfig {}
复制代码


  • 创建 Controller 类 HelloController ,用于验证 CustomizeService1 等接口的实例是否可用:


package com.bolingcavalry.customizeimport.controller;
import com.bolingcavalry.customizeimport.service.CustomizeService1;import com.bolingcavalry.customizeimport.service.CustomizeService2;import com.bolingcavalry.customizeimport.service.CustomizeService3;import com.bolingcavalry.customizeimport.service.CustomizeService4;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;
@RestControllerpublic class HelloController { @Autowired(required = false) CustomizeService1 customizeServiceImpl1;
@Autowired(required = false) CustomizeService2 customizeServiceImpl2;
@Autowired(required = false) CustomizeService3 customizeServiceImpl3;
@Autowired(required = false) CustomizeService4 customizeServiceImpl4;
@GetMapping("hello") public String hello(){ customizeServiceImpl1.execute(); customizeServiceImpl2.execute(); customizeServiceImpl3.execute(); customizeServiceImpl4.execute(); return "finish"; }}
复制代码


  • 最后是启动类 CustomizeimportApplication:


package com.bolingcavalry.customizeimport;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class CustomizeimportApplication {
public static void main(String[] args) { SpringApplication.run(CustomizeimportApplication.class, args); }}
复制代码


  • 启动应用,观察日志,可见 Import 的值都生效了,ImportSelector 的 selectImports 被调用,ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 被调用,然后是 CustomizeServiceImpl1 等类的实例被创建,篇幅所限就不在此将所有日志贴出,请您自行运行和检查:


2018-09-10 12:36:33.917  INFO 5884 --- [           main] c.b.customizeimport.util.Utils           : selectImports : CustomizeImportSelector************************************************************java.lang.Thread.getStackTrace() 1,556 <- com.bolingcavalry.customizeimport.util.Utils.printTrack() 15 <- com.bolingcavalry.customizeimport.selector.CustomizeImportSelector.selectImports() 16 <- org.springframework.context.annotation.ConfigurationClassParser.processImports() 591 <- org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass() 304 <- org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass() 247 <- org.springframework.context.annotation.ConfigurationClassParser.parse() 192 <- org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass() 297 <- org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass() 247 <- org.springframework.context.annotation.ConfigurationClassParser.parse() 200 <- org.springframework.context.annotation.ConfigurationClassParser.parse() 169 <- org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions() 308 <- org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry() 228 <- org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors() 272 <- org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors() 92 <- org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors() 687 <- org.springframework.context.support.AbstractApplicationContext.refresh() 525 <- org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh() 122 <- org.springframework.boot.SpringApplication.refresh() 693 <- org.springframework.boot.SpringApplication.refreshContext() 360 <- org.springframework.boot.SpringApplication.run() 303 <- org.springframework.boot.SpringApplication.run() 1,118 <- org.springframework.boot.SpringApplication.run() 1,107 <- com.bolingcavalry.customizeimport.CustomizeimportApplication.main() 10************************************************************2018-09-10 12:36:34.262  INFO 5884 --- [           main] c.b.customizeimport.util.Utils           : selectImports : CustomizeDeferredImportSelector.........************************************************************
复制代码


  • 在浏览器输入:http://localhost:8080/hello,检查控制台日志,发现 CustomizeServiceImpl1 等的 execute 方法都被顺利执行,可见所有实例都在 spring 容器内:


execute : CustomizeServiceImpl1execute : CustomizeServiceImpl2execute : CustomizeServiceImpl3execute : CustomizeServiceImpl4
复制代码


  • 至此,@Import 注解的源码分析和实战都完成了,该注解经常会用到,希望本文能帮您更加深入的了解这一功能,助您在开发和设计中对 bean 的注册和管理操作更加得心应手;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 2022 年 06 月 19 日阅读数: 30
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
spring4.1.8扩展实战之八:Import注解_Java_程序员欣宸_InfoQ写作社区