spring4.1.8扩展实战之八:Import注解
在spring框架下做开发时,@Import是常见的注解,可以用来动态创建bean,今天我们先从源码分析原理,再用实战来验证Import的作用;
原文地址:https://blog.csdn.net/boling_cavalry/article/details/82530167
文章概览
本章由以下几部分组成:
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
从以上代码可见,使异步调用生效的关键是@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方法,如下图红框所示:
2. 展开invokeBeanFactoryPostProcessors方法,继续追踪到了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors方法,具体操作如下图红框所示:
3. 对于上图分析的调用invokeBeanDefinitionRegistryPostProcessors方法时作为入参传入的bean,ConfigurationClassPostProcessor类的实例是符合过滤要求的:既实现了BeanDefinitionRegistryPostProcessor接口,又实现了PriorityOrdered接口,因此,在invokeBeanDefinitionRegistryPostProcessors方法中,ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法被调用:
4. ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry方法中,如下图红框所示,processConfigBeanDefinitions方法负责处理@Configuration注解相关的业务:
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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
6. 看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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
7. 从上述代码中可以看出,DeferredImportSelector实现类的selectImports方法会在最后被调用,其余的关键逻辑应该在parse(AnnotationMetadata metadata, String beanName)这个关键方法中,顺着这个方法一直追踪下去,直到doProcessConfigurationClass方法,如下图红框所示,所有Import注解的处理,都在processImports方法中:
8. processImports方法中包含了对ImportSelector实现类和ImportBeanDefinitionRegistrar实现类的处理,以及未实现这些接口的类的处理,我们逐个来分析吧,首先看ImportBeanDefinitionRegistrar实现类的处理,如下图红框位置,调用configClass.addImportBeanDefinitionRegistrar方法将ImportBeanDefinitionRegistrar实现类存入configClass的成员变量importBeanDefinitionRegistrars中,后面的ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,this.reader.loadBeanDefinitions(configClasses);会调用这些ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法:
9. 再来看processImports方法中对ImportSelector实现类的处理,这里略有些复杂,因为涉及到对processImports方法的迭代调用,请看下图红框旁边的红字说明:
如上图所示,第二步就是在processImports方法中调用了processImports方法,再次进入processImports之后,会着ImportSelector实现类返回的bean名称直接走到第三步的位置,第三步处理的就是没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean了;
10. processImports方法对没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean的处理是执行processConfigurationClass方法,将这些bean放入了成员变量configurationClasses中,如下图红框所示:
11. processImports方法分析完毕,Import注解导入的bean都被保存在ConfigurationClassParser实例中,我们回到ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法,如下图,this.reader.loadBeanDefinitions(configClasses);负责处理processImports方法找出的那些打算通过@Import注解来注册到spring容器的bean:
12. 展开this.reader.loadBeanDefinitions(configClasses)方法,在ConfigurationClassBeanDefinitionReader类中,是对每个配置类逐个执行loadBeanDefinitionsForConfigurationClass方法:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
13. 展开方法,真相大白,实现了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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
14. 前面将普通类、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方法):
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
至此,源码分析工作已经结束,接下来实战@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) | aaa@qq.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
这个git项目中有多个文件夹,本章源码在文件夹customizeimport下,如下图红框所示:
下面动手开发:
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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
3. 创建四个接口,除了类名不同,其他的都一样,如下:
package com.bolingcavalry.customizeimport.service;
public interface CustomizeService1 {
void execute();
}
- 1
- 2
- 3
- 4
- 5
4. 创建接口实现类,除了类名不同,其他的都一样,如下:
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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
5. 创建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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
6. 创建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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
7. 创建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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
8. 创建配置类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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
9. 创建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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
10. 最后是启动类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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
11. 启动应用,观察日志,可见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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
12. 在浏览器输入:http://localhost:8080/hello,检查控制台日志,发现CustomizeServiceImpl1等的execute方法都被顺利执行,可见所有实例都在spring容器内:
execute : CustomizeServiceImpl1
execute : CustomizeServiceImpl2
execute : CustomizeServiceImpl3
execute : CustomizeServiceImpl4
- 1
- 2
- 3
- 4
至此,@Import注解的源码分析和实战都完成了,该注解经常会用到,希望本文能帮您更加深入的了解这一功能,助您在开发和设计中对bean的注册和管理操作更加得心应手;