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

简介: spring框架下做开发时,@Import是常见的注解,可以用来动态创建bean,今天我们先从源码分析原理,再用实战来验证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方法,如下图红框所示:

image.png

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

image.png

  • 对于上图分析的调用invokeBeanDefinitionRegistryPostProcessors方法时作为入参传入的bean,ConfigurationClassPostProcessor类的实例是符合过滤要求的:既实现了BeanDefinitionRegistryPostProcessor接口,又实现了PriorityOrdered接口,因此,在invokeBeanDefinitionRegistryPostProcessors方法中,ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法被调用:
  • ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法中,如下图红框所示,processConfigBeanDefinitions方法负责处理@Configuration注解相关的业务:

image.png

  • 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方法中:

image.png

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

image.png

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

image.png

  • 如上图所示,第二步就是在processImports方法中调用了processImports方法,再次进入processImports之后,会着ImportSelector实现类返回的bean名称直接走到第三步的位置,第三步处理的就是没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean了;
  • processImports方法对没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean的处理是执行processConfigurationClass方法,将这些bean放入了成员变量configurationClasses中,如下图红框所示:

image.png

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

image.png

  • 展开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

image.png

上图红框中的描述会让我们不禁疑惑: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下载源码,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本章源码在文件夹customizeimport下,如下图红框所示:

image.png

  • 下面动手开发:
  • 设计: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方法中注册
  • 创建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;

@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";
    }
}
  • 最后是启动类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);
    }
}
  • 启动应用,观察日志,可见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 : CustomizeServiceImpl1
execute : CustomizeServiceImpl2
execute : CustomizeServiceImpl3
execute : CustomizeServiceImpl4
  • 至此,@Import注解的源码分析和实战都完成了,该注解经常会用到,希望本文能帮您更加深入的了解这一功能,助您在开发和设计中对bean的注册和管理操作更加得心应手;

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关文章
|
15天前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
36 0
|
1月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
|
22天前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
44 4
SpringBoot必须掌握的常用注解!
|
23天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
79 2
|
23天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
34 1
|
1月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
128 6
|
1月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
84 2
|
18天前
|
存储 安全 Java
springboot当中ConfigurationProperties注解作用跟数据库存入有啥区别
`@ConfigurationProperties`注解和数据库存储配置信息各有优劣,适用于不同的应用场景。`@ConfigurationProperties`提供了类型安全和模块化的配置管理方式,适合静态和简单配置。而数据库存储配置信息提供了动态更新和集中管理的能力,适合需要频繁变化和集中管理的配置需求。在实际项目中,可以根据具体需求选择合适的配置管理方式,或者结合使用这两种方式,实现灵活高效的配置管理。
13 0
|
30天前
|
存储 Java 数据管理
强大!用 @Audited 注解增强 Spring Boot 应用,打造健壮的数据审计功能
本文深入介绍了如何在Spring Boot应用中使用`@Audited`注解和`spring-data-envers`实现数据审计功能,涵盖从添加依赖、配置实体类到查询审计数据的具体步骤,助力开发人员构建更加透明、合规的应用系统。
|
1月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
71 0
下一篇
无影云桌面