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

2018年09月10日 12:53:57 博陵精骑 阅读数:441更多

 
 版权声明:欢迎转载,请注明出处,谢谢。 https://blog.csdn.net/boling_cavalry/article/details/82530167

在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) git@github.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的注册和管理操作更加得心应手;

最新文章

  1. BZOJ1572 工作安排 USACO2009
  2. spring装配---处理自动装配的歧义性
  3. javascript父级鼠标移入移出事件中的子集影响父级的处理方法
  4. 【caffe】mnist训练日志
  5. 76 binary_search 查找重复元素
  6. makefile多目录的.c 格式.cpp混合编译
  7. AC日记——神奇的幻方 洛谷 P2615(大模拟)
  8. Installing Lua in Mac
  9. BootStrap2学习日记12---注册表单
  10. 【JSP实例】指定用户计数器
  11. Normalize.css 介绍与源码解读
  12. allegro 导Gerber文件
  13. mac下eclipse安装svn插件-subclipse
  14. javamelody 使用
  15. canvas描绘渐变的矩形
  16. 使用Glide以及OkHttp集成
  17. win10家庭版升级专业版的两种方法和密钥
  18. 阿里云 IOT 对接设备开发 C# 开发设备对接阿里云 IOT平台
  19. Day 43数据库(Day1)
  20. Python3基础 print 输出helloworld

热门文章

  1. PCB电路设计 altiumdesigner(项目软件总结)
  2. sql中的sp_helptext、sp_help 、sp_depends
  3. 教你使用Python制作酷炫二维码
  4. 最全的 Java 知识总结- Github 日增 10 star
  5. Elasticsearch7.X 入门学习第七课笔记-----Mapping多字段与自定义Analyzer
  6. C# http post请求帮助类
  7. vue传值(小demo)
  8. Python中queue消息队列模块
  9. 通过busybox制作根文件系统
  10. Jquery异步上传文件