天天看点

Spring注解[email protected]注解

今日在查看springboot文档时看到

None of these features are mandatory and you may choose to replace this single annotation by any of the features that it enables. For instance, you may not want to use component scan in your application:

package com.example.myapplication;

import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@EnableAutoConfiguration
@Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {

public static void main(String[] args) {
   	SpringApplication.run(Application.class, args);
}

}
           

In this example, Application is just like any other Spring Boot application except that @Component-annotated classes are not detected automatically and the user-defined beans are imported explicitly (see @Import).

在查看springboot文档时看到

None of these features are mandatory and you may choose to replace this single annotation by any of the features that it enables. For instance, you may not want to use component scan in your application:

package com.example.myapplication;import org.springframework.boot.SpringApplication;import org.springframework.context.annotation.ComponentScanimport org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Import;@[email protected]@Import({ MyConfig.class, MyAnotherConfig.class })public class Application {public static void main(String[] args) { SpringApplication.run(Application.class, args);}}

In this example, Application is just like any other Spring Boot application except that @Component-annotated classes are not detected automatically and the user-defined beans are imported explicitly (see @Import).

初探

初始化spring boot,包含web即可

分别创建类

public class Aaaaaaa {
    public Aaaaaaa(){
        System.out.println("Aaaaaaa is init   *********************************");
    }
}

public class Bbbbbbb {
    public Bbbbbbb(){
        System.out.println("Bbbbbbb is init ***********************************");
    }
}

@Configuration
@Import(
.class)
public class MyConfig {

    public MyConfig(){
        System.out.println("MyConfig     ***********************************************");
    }

    @Bean
    public Aaaaaaa aaaaaaa(){
        Aaaaaaa aa=new Aaaaaaa();
        System.out.println("MyConfig   aaaaaaa  ***********************************************");
        return aa;
    }
}
           

运行应用将得到如下输出

2019-09-18 17:06:27.132  INFO 14084 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-09-18 17:06:27.133  INFO 14084 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1093 ms
MyConfig     ***********************************************
Bbbbbbb is init ***********************************
Aaaaaaa is init   *********************************
MyConfig   aaaaaaa  ***********************************************
           

通过输出将发现@Import导入的类被spring加载并创建了,且多次运行都会发现Bbbbbbb的实例化是在Aaaaaaa之前的,在MyConfig之后的,将@Import(Bbbbbbb.class)改至Aaaaaaa上

@Import(Bbbbbbb.class)
public class Aaaaaaa {
    public Aaaaaaa(){
        System.out.println("Aaaaaaa is init   *********************************");
    }
}
           

会发现Bbbbbbb并未创建

专业点的说明:

本章由以下几部分组成:

  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容器有以下四种处理方式:

  1. 如果Abc类实现了ImportSelector接口,spring容器就会实例化Abc类,并且调用其selectImports方法;
  2. DeferredImportSelector是ImportSelector的子类,如果Abc类实现了DeferredImportSelector接口,spring容器就会实例化Abc类,并且调用其selectImports方法,和ImportSelector的实例不同的是,DeferredImportSelector的实例的selectImports方法调用时机晚于ImportSelector的实例,要等到@Configuration注解中相关的业务全部都处理完了才会调用(具体逻辑在ConfigurationClassParser.processDeferredImportSelectors方法中),想了解更多DeferredImportSelector和ImportSelector的区别,请参考《ImportSelector与DeferredImportSelector的区别(spring4) 》;
  3. 如果Abc类实现了ImportBeanDefinitionRegistrar接口,spring容器就会实例化Abc类,并且调用其registerBeanDefinitions方法;
  4. 如果Abc没有实现ImportSelector、DeferredImportSelector、ImportBeanDefinitionRegistrar等其中的任何一个,spring容器就会实例化Abc类,官方说明在这里;
分析spring源码,揭示Import注解的工作原理

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

  1. 先看spring容器的初始化代码,定位AbstractApplicationContext类的refresh方法,里面会调用invokeBeanFactoryPostProcessors方法,如下图红框所示:
    Spring注解[email protected]注解
  2. 展开invokeBeanFactoryPostProcessors方法,继续追踪到了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法,具体操作如下图红框所示:
    Spring注解[email protected]注解
  3. 对于上图分析的调用invokeBeanDefinitionRegistryPostProcessors方法时作为入参传入的bean,ConfigurationClassPostProcessor类的实例是符合过滤要求的:既实现了BeanDefinitionRegistryPostProcessor接口,又实现了PriorityOrdered接口,因此,在invokeBeanDefinitionRegistryPostProcessors方法中,ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法被调用:
  4. ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法中,如下图红框所示,processConfigBeanDefinitions方法负责处理@Configuration注解相关的业务:
    Spring注解[email protected]注解
  5. 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();
           
  1. 看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();
    }
           
  1. 从上述代码中可以看出,DeferredImportSelector实现类的selectImports方法会在最后被调用,其余的关键逻辑应该在parse(AnnotationMetadata metadata, String beanName)这个关键方法中,顺着这个方法一直追踪下去,直到doProcessConfigurationClass方法,如下图红框所示,所有Import注解的处理,都在processImports方法中:
    Spring注解[email protected]注解
  2. processImports方法中包含了对ImportSelector实现类和ImportBeanDefinitionRegistrar实现类的处理,以及未实现这些接口的类的处理,我们逐个来分析吧,首先看ImportBeanDefinitionRegistrar实现类的处理,如下图红框位置,调用configClass.addImportBeanDefinitionRegistrar方法将ImportBeanDefinitionRegistrar实现类存入configClass的成员变量importBeanDefinitionRegistrars中,后面的ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,this.reader.loadBeanDefinitions(configClasses);会调用这些ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法:
    Spring注解[email protected]注解
  3. 再来看processImports方法中对ImportSelector实现类的处理,这里略有些复杂,因为涉及到对processImports方法的迭代调用,请看下图红框旁边的红字说明:
    Spring注解[email protected]注解
    如上图所示,第二步就是在processImports方法中调用了processImports方法,再次进入processImports之后,会着ImportSelector实现类返回的bean名称直接走到第三步的位置,第三步处理的就是没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean了;
  4. processImports方法对没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean的处理是执行processConfigurationClass方法,将这些bean放入了成员变量configurationClasses中,如下图红框所示:
    Spring注解[email protected]注解
  5. processImports方法分析完毕,Import注解导入的bean都被保存在ConfigurationClassParser实例中,我们回到ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法,如下图,this.reader.loadBeanDefinitions(configClasses);负责处理processImports方法找出的那些打算通过@Import注解来注册到spring容器的bean:
    Spring注解[email protected]注解
  6. 展开this.reader.loadBeanDefinitions(configClasses)方法,在ConfigurationClassBeanDefinitionReader类中,是对每个配置类逐个执行loadBeanDefinitionsForConfigurationClass方法:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
        TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
        for (ConfigurationClass configClass : configurationModel) {
            loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
        }
    }
           
  1. 展开方法,真相大白,实现了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());
    }
           
  1. 前面将普通类、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是如何做到的呢?一起来看源码吧:

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

2. 展开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容器

下面动手开发:

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

    接口 实现类 注册方式

    CustomizeService1 CustomizeService1Impl1 CustomizeService1Impl1.class作为Import注解的值

    CustomizeService2 CustomizeService1Impl2 作为ImportSelector实现类的selectImports方法的返回值

    CustomizeService3 CustomizeService1Impl3 作为DeferredImportSelector实现类的selectImports方法的返回值

    CustomizeService4 CustomizeService1Impl4 在ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法中注册

  2. 创建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>
           
  1. 创建接口实现类,除了类名不同,其他的都一样,如下:
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());
    }
}
           
  1. 创建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"};
    }
}
           
  1. 创建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"};
    }
}
           
  1. 创建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);
        }
    }
}
           
  1. 创建配置类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 {
}
           
  1. 创建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;

@RestController
public 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";
    }
}
           
  1. 最后是启动类CustomizeimportApplication:
package com.bolingcavalry.customizeimport;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CustomizeimportApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomizeimportApplication.class, args);
    }
}
           
  1. 启动应用,观察日志,可见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
...
...
...
           
  1. 在浏览器输入:http://localhost:8080/hello,检查控制台日志,发现CustomizeServiceImpl1等的execute方法都被顺利执行,可见所有实例都在spring容器内:

execute : CustomizeServiceImpl1

execute : CustomizeServiceImpl2

execute : CustomizeServiceImpl3

execute : CustomizeServiceImpl4

继续阅读