@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中也会共享 ...
随机推荐
- Linux Shell中的变量声明和一些特殊变量
在SHELL中定义变量比较直接,无类型区别,不需要像Java那样定义好是String还是int等. 声明变量需要遵守或者注意的几点: 变量名和等号之间不能有空格. 变量名首字符必须为字母. 变量名里可 ...
- 使用pycharm开发web——django2.1.5(一)入坑尝试第一步,基本搭建
首先,接触python的人应该都会用pip 来安装需要的包吧(------>>>>)默认 在运行中使用python -m django --version来检查自己的djang ...
- java中public protected friendly private作用域
1.public:public表明该数据成员.成员函数是对所有用户开放的,所有用户都可以直接进行调用 2.private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直 ...
- 【剑指OFFER】链表中倒数第k个结点
[问题描述] 输入一个链表,输出该链表中倒数第k个结点. 时间限制:1秒 空间限制:32768K [AC代码] p先走k步,q再走,这样p和q的距离就是k了,等p走到尽头,那么q自然就到了倒数第k个位 ...
- [DEBUG] Spring boot前端html无法下载示例文件
更新:原方法打jar包的时候是可以的,后来我打war包之后下载的文件就是0字节.尴尬:) 所以现在更换一种方法,然后打war包.在服务器已测试成功. 前端不需要改变,只需要更改controller: ...
- ubuntu 忘记密码如何 修改密码
ubuntu 忘记密码如何 修改密码 这个链接讲的很不错 https://blog.csdn.net/zd147896325/article/details/81664558 本来我只是玩一玩,但是我 ...
- 笔记-4:python组合数据类型
1.字符串(str) 字符串是字符的序列表示, 根据字符串的内容多少分为单行字符串和多行字符串. 单行字符串可以由一对单引号(') 或双引号(")作为边界来表示, 单引号和双引号作用相同. ...
- python+selenium+webdriver+BeautifulSoup实现自动登录
from selenium import webdriverimport timefrom bs4 import BeautifulSoupfrom urllib import requestimpo ...
- mysqlpump原理及实战
MySQL5.7之后多了一个备份工具:mysqlpump.它是mysqldump的一个衍生,mysqldump就不多说明了,现在看看mysqlpump到底有了哪些提升,可以查看官方文档,这里针对如何使 ...
- .Net C# Dictionary 和参数字符串互转
#region Parse #region Dictionary Parse To String /// <summary> /// Dictionary Parse To String ...