版权声明:欢迎转载,请注明出处,谢谢。 https://blog.csdn.net/boling_cavalry/article/details/82530167
在spring框架下做开发时,@Import是常见的注解,可以用来动态创建bean,今天我们先从源码分析原理,再用实战来验证Import的作用;
原文地址:https://blog.csdn.net/boling_cavalry/article/details/82530167
本章由以下几部分组成:
有很多注解都以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(Abc.class),根据类Abc的不同类型,spring容器有以下四种处理方式:
接下来通过spring源码来了解spring容器是如何处理Import注解的;
//被确认为配置类的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();
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();
}
如上图所示,第二步就是在processImports方法中调用了processImports方法,再次进入processImports之后,会着ImportSelector实现类返回的bean名称直接走到第三步的位置,第三步处理的就是没有实现ImportSelector和ImportBeanDefinitionRegistrar这些接口的普通bean了;
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
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());
}
至此,通过Import注解注册bean的四种方式已经全部分析完毕,小结如下:
在官方API文档中,对ImportSelector接口的描述如下图所示,红框中的一段意思是: ImportSelector接口的实现类,如果同时也实现了EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware这些接口中的一个或几个,那么这些接口对应的方法优先执行,然后才会执行ImportSelector接口的selectImports:
上图红框中的描述会让我们不禁疑惑:spring是如何做到的呢?一起来看源码吧:
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注解的使用;
到了实战验证环节了,本次实战的内容是创建一个springboot工程,通过@Import注解将bean注册到spring容器,如果您不想敲代码,也可以去github下载源码,地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | 该项目在GitHub上的主页 | |
git仓库地址(https) | 该项目源码的仓库地址,https协议 | |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
这个git项目中有多个文件夹,本章源码在文件夹customizeimport下,如下图红框所示:
下面动手开发:
接口 | 实现类 | 注册方式 |
---|---|---|
CustomizeService1 | CustomizeService1Impl1 | CustomizeService1Impl1.class作为Import注解的值 |
CustomizeService2 | CustomizeService1Impl2 | 作为ImportSelector实现类的selectImports方法的返回值 |
CustomizeService3 | CustomizeService1Impl3 | 作为DeferredImportSelector实现类的selectImports方法的返回值 |
CustomizeService4 | CustomizeService1Impl4 | 在ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法中注册 |
<?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());
}
}
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"};
}
}
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"};
}
}
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);
}
}
}
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 {
}
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";
}
}
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);
}
}
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
...
...
...
************************************************************
execute : CustomizeServiceImpl1
execute : CustomizeServiceImpl2
execute : CustomizeServiceImpl3
execute : CustomizeServiceImpl4
至此,@Import注解的源码分析和实战都完成了,该注解经常会用到,希望本文能帮您更加深入的了解这一功能,助您在开发和设计中对bean的注册和管理操作更加得心应手;