@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源码版本:5.0.5.RELEASE
跟踪spring容器是如何处理Import注解的,容器初始化一般从AbstractApplicationContext类的refresh开始,其它过程跳过,直接通过堆栈到相关的地方;
doProcessConfigurationClass:, ConfigurationClassParser {org.springframework.context.annotation}
processConfigurationClass:, ConfigurationClassParser {org.springframework.context.annotation}
parse:, ConfigurationClassParser {org.springframework.context.annotation}
doProcessConfigurationClass:, ConfigurationClassParser {org.springframework.context.annotation}
processConfigurationClass:, ConfigurationClassParser {org.springframework.context.annotation}
parse:, ConfigurationClassParser {org.springframework.context.annotation}
parse:, ConfigurationClassParser {org.springframework.context.annotation}
processConfigBeanDefinitions:, ConfigurationClassPostProcessor {org.springframework.context.annotation}
postProcessBeanDefinitionRegistry:, ConfigurationClassPostProcessor {org.springframework.context.annotation}
invokeBeanDefinitionRegistryPostProcessors:, PostProcessorRegistrationDelegate {org.springframework.context.support}
invokeBeanFactoryPostProcessors:, PostProcessorRegistrationDelegate {org.springframework.context.support}
invokeBeanFactoryPostProcessors:, AbstractApplicationContext {org.springframework.context.support}
refresh:, AbstractApplicationContext {org.springframework.context.support}
ConfigurationClassParser是解析@PropertySources,@ComponentScan,@Import,@ImportResource,@Bean注解的地方
在ConfigurationClassParser#parse中
public void parse(Set<BeanDefinitionHolder> configCandidates) {
//稍后执行的parse方法中,所有DeferredImportSelector实现类都会被放入集合deferredImportSelectors中
this.deferredImportSelectors = new LinkedList<>(); 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 (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
} //此方法内,会将集合deferredImportSelectors中的所有对象取出来执行其selectImports方法
processDeferredImportSelectors();
}
查看处理@import的地方
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
...... // Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true); ......
}
跟踪查看getImports就是递归取类的@Import注解的,取到后在processImports方法中进行处理:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) {
return;
} if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//如果是ImportSelector接口的实现类,就在此处理
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
//实例化这些ImportSelector的实现类
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
//如果这实现类还实现了BeanFactoryAware、EnvironmentAware这些接口,就要先执行这些接口中声明的方法
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
//如果这个实现类也实现了DeferredImportSelector接口,就被加入到集合deferredImportSelectors中,在解析完成后在执行
if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(
new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
//注意,这一行是关键代码!!!执行实现类的selectImports方法
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
//处理ImportBeanDefinitionRegistrar的实现类
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
//configClass.addImportBeanDefinitionRegistrar方法将ImportBeanDefinitionRegistrar实现类存入configClass的成员变量importBeanDefinitionRegistrars中,
//后面的ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法中,处理完parser.parse后在执行
//this.reader.loadBeanDefinitions(configClasses);会调用这些ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
//普通类
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
小结如下:
- 1. 普通类(即没有实现ImportBeanDefinitionRegistrar、ImportSelector、DeferredImportSelector等接口的类)会通过ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromImportedResources方法将bean定义注册到spring容器;
- 2. ImportSelector实现类,其selectImports方法返回的bean的名称,通过ConfigurationClassParser类的asSourceClass方法转成SourceClass对象,然后被当作普通类处理;
- 3. ImportSelector与DeferredImportSelector的区别,就是selectImports方法执行时机有差别,这个差别期间,spring容器对此Configguration类做了些其他的逻辑:包括对@ImportResource、@Bean这些注解的处理(注意,这里只是对@Bean修饰的方法的处理,并不是立即调用@Bean修饰的方法,这个区别很重要!);
- 4. ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions方法会被调用,里面可以注册业务所需的bean定义;
@Import注解的作用的更多相关文章
- EnableAutoConfiguration注解 Spring中@Import注解的作用和使用
EnableAutoConfiguration注解 http://www.51gjie.com/javaweb/1046.html springboot@EnableAutoConfiguration ...
- Spring框架中的org.springframework.context.annotation.Import注解类
@Import注解的作用和在使用spring的xml配置时用到的<import/>类似.但应注意是@Import在使用时必须要保证能被IOC容器扫描到,所以通常它会和@Configurat ...
- 五、Spring中的@Import注解
一.使用@Import注解导入组件 @Import注解的作用是给容器中导入组件,回顾下我们给容器中导入组件的方式,可以通过Spring的xm配置方式,可以通过注解,如@Component等,也可以通过 ...
- Spring Import注解
今天了解了,Spring @Import的使用 先贴上Spring官方关于Spring @Import注解的文档链接 https://docs.spring.io/spring/docs/3.0. ...
- spring4.1.8扩展实战之八:Import注解
spring4.1.8扩展实战之八:Import注解 2018年09月10日 12:53:57 博陵精骑 阅读数:441更多 所属专栏: spring4源码分析与实战 版权声明:欢迎转载,请注明 ...
- 【Spring注解驱动开发】在@Import注解中使用ImportSelector接口导入bean
写在前面 在上一篇关于Spring的@Import注解的文章<[Spring注解驱动开发]使用@Import注解给容器中快速导入一个组件>中,我们简单介绍了如何使用@Import注解给容器 ...
- 注解式项目开发!详细解析Java中各个注解的作用和使用方式
@Target 作用: 指明了修饰的这个注解的使用范围, 即被描述的注解可以用在哪里 @Target(ElementType.Type) ElementType取值的类型: TYPE: 类,接口或者枚 ...
- paip.Java Annotation注解的作用and 使用
paip.Java Annotation注解的作用and 使用 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog. ...
- @ModelAttribute注解的作用
@ModelAttribute注解的作用:1.放在方法上注解不带属性: 方法无返回值: 执行其他方法时,先执行该注解标记方法. 如果方法中有将一些属性放入model的操作,其他方法model中也会共享 ...
随机推荐
- #【Python】【基础知识】【内置对象常用方法】
数字 数字的常用方法: >>> dir(int) ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class ...
- mysql 连接远程连接服务器 1130错误
今天在用sqlyog连接非本地的Mysql服务器的数据库,居然无法连接很奇怪,报1130错误, ERROR 1130: Host 192.168.3.100 is not allowed to con ...
- 【bitset】Kth Minimum Clique
#include<bits/stdc++.h> #define B bitset<105> using namespace std; typedef long long ll ...
- k8s之资源指标API部署metrics-server
1.部署metrics-server 从v1.8开始,引入了新的功能,即把资源指标引入api,资源指标:metrics-server,自定义指标:prometheus,k8s-prometheus-a ...
- 怎样在页面关闭时发起HTTP请求
比如有需求是要让页面关闭时, 在数据库中记录用户的一些数据或log日志. 这时就需要在用户关闭页面时发起HTTP请求. 做法是对window.onunload设置事件监听函数, 在函数内发起AJAX请 ...
- POJ 1860 汇率 SPFA
题意 有多种汇币,汇币之间可以交换,这需要手续费,当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 ...
- C# DataTable根据字段排序
DataTable dt = new DataTable(); dt.Columns.Add("Name"); dt.Columns.Add("Age");// ...
- c++11 用户定义字面量
c++11 用户定义字面量 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...
- # 使用scatter()绘制散点图
使用scatter()绘制散点图 之前写过一篇,使用magic function快速绘图的教程了:https://www.cnblogs.com/jiading/p/11750001.html.但这种 ...
- idea的EasyCode使用
EasyCode可以自动根据表格生成:entity,dao,service,serviceImpl,controller 使用方法: 一.安装EasyCode插件: File-setting-Plug ...