文档章节

第五章 spring-connet之Imports注解来龙去脉

鸟菜啊
 鸟菜啊
发布于 09/22 09:19
字数 860
阅读 22
收藏 1

前言

imports是一个在spring体系里非常重要的注解,基本每个Enable开头的注解必然有一个import注解。接下来我们深入研究下import的作用。看小节的同学建议先取看PostProcessorRegistrationDelegate与BeanFactoryPostProcessor体系AnnotationConfigUtils

PS: 可以先看这个博客了解下Spring Import 三种用法与源码解读

解析

ConfigurationClassParser的processImports方法是最核心的方法

	// 这里是从SourceClass获得imports注解,注意jdk8允许标记多个注解,一个class可以标记多个不同注解,其他注解上也可以标记imports注解,所以需要一个set存放
	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		collectImports(sourceClass, imports, visited);
		return imports;
	}
	
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates,boolean checkForCircularImports) throws IOException {

		if (importCandidates.isEmpty()) {
			return;
		}

		if (checkForCircularImports &amp;&amp; 
this.importStack.contains(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)) {
						Class<?> candidateClass = candidate.loadClass();
						ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
						invokeAwareMethods(selector);
						// 判断是否是需要延迟加载,继承DeferredImportSelector类的都会延迟加载
						if (this.deferredImportSelectors != null &amp;&amp; selector instanceof DeferredImportSelector) {
							this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						}
						else {
							// 执行ImportSelector的实现类,返回需要加载的内容
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					}
					else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						// 这个是非常重要的,ImportBeanDefinitionRegistrar.addImportBeanDefinitionRegistrar方法的形参是registrar。
						Class<?> candidateClass = candidate.loadClass();
						ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass,ImportBeanDefinitionRegistrar.class);
						invokeAwareMethods(registrar);
						configClass.addImportBeanDefinitionRegistrar(registrar, 
currentSourceClass.getMetadata());
					}
					else {
						// 
如果都不是,就调动processConfigurationClass(迭代了,processImports上两级的调用就是
processConfigurationClass)
						this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
						processConfigurationClass(candidate.asConfigClass(configClass));
					}
				}
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Exception ex) {
				throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configClass.getMetadata().getClassName() + "]", ex);
			}
			finally {
				this.importStack.pop();
			}
		}
	}

重点

ImportSelector

看ImportSelector.selectImports 就知道返回一个字符串数组,字符串数组是class的完全限定名,进行processImports扫描

public interface ImportSelector {
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}


String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection&lt;SourceClass&gt; importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar的重要行比ImportSelector高,最重要的是形参 BeanDefinitionRegistry,从下面代码BeanPostProcessorsRegistrar对象可以看 到,ImportBeanDefinitionRegistrar具备向容器里面注册bean的能力。可以添加想要的bean,完成想要的功能。

public interface ImportBeanDefinitionRegistrar {
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}

public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws 
BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
	if (this.beanFactory == null) {
				return;
	}
			 registerSyntheticBeanIfMissing(registry,"webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,"errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,String name, Class&lt;?&gt; beanClass) {
			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

得到ImportBeanDefinitionRegistrar不会立即执行,会保存到一个集合中,一起执行

ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, 
ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

public void  addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
		this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}

执行时机,是等待所以初始化的注解或者class解析完成

只有一次出发机会

ConfigurationClassPostProcessor对象只会被执行一次,这次只会解析那些内容。请看下面的代码,只会解析UserConsumeApplication.class的所有注解,所以有一些imports注解是不会被扫描,执行到的。自己也被坑过一次,以为自己的imports会被扫描并且执行,搞了好久,真坑啊。

@SpringBootApplication
public class UserConsumeApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserConsumeApplication.class, args);
    }
}

#######

坐在一个很小的公园的花坛上写完。

© 著作权归作者所有

鸟菜啊
粉丝 39
博文 48
码字总数 75835
作品 0
深圳
项目经理
私信 提问
第五章 spring-connet之AnnotationConfigUtils

前言 AnnotatedBeanDefinitionReader是AnnctionConfig相关上下文与AnnotationConfigUtils的桥梁。还有一个作用是解析class成为BeanDefinitionHolder,注册到容器里面。AnnotationConfigUtils...

鸟菜啊
09/20
39
0
第三章 spring-bean之AutowireCapableBeanFactory(6)

前言 AutowireCapableBeanFactory接口的实现类是AbstractAutowireCapableBeanFactory。AutowireCapableBeanFactory接口的作用是通过多种方式创建bean,执行bean的生命周期,bean处理,依赖解...

鸟菜啊
2018/08/04
1K
0
第五章 spring-context之ApplicationContext体系

前言 ApplicationContext(应用上下文)是使用spring框架的入口。深入理解,运用ApplicationContext的每个细节。会对spring的扩展,解决一些细节行问题。有巨大的帮助 ApplicationContext接口...

鸟菜啊
04/22
62
0
第五十二章:基于SpringBoot2使用Rest访问MongoDB数据

在之前项目中我们想要读取内的内容需要使用来完成数据的,那如果我们想要通过的形式获取内的数据就更麻烦了,还需要自行去创建对应的控制器,然后使用从内读取出数据后返回给前端。 在上一章...

恒宇少年
2018/04/22
0
0
Spring Boot [集成-MyBatis]

导读: 在上篇文章中我们介绍了spring-data-jpa的一些常用方法,在这篇文章中我们在介绍关于mybatis与Spring Boot 的集成,及一些常用方法 集成: 这里有两种方式,一种是常规的Spring 应用的...

yangrd
2018/08/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

带你了解 Java内存模型

Java内存模型的规定: 1、所有变量存储在主内存中; 2、每个线程都有自己的工作内存,且对变量的操作都是在工作内存中进行; 3、不同线程之间无法直接访问彼此工作内存中的变量,要想访问只能...

linux-tao
11分钟前
2
0
.net c# datetime转string 时间转字符串

.net c# datetime转string 时间转字符串 .net c# datetime转string 时间转字符串 刚开始接触net 时间转换字符串 一搜索出来的全是 字符串转时间,要么就是系统当前时间转字符串 就没有一个指...

青峰Jun19er
12分钟前
2
0
hbase demo

HbaseDao public class HbaseDao {@Testpublic void insertTest() throws Exception {Configuration conf = HBaseConfiguration.create();conf.set("hbase.zookeeper.qu......

Garphy
22分钟前
2
0
IT兄弟连 HTML5教程 HTML5表单 多样的输入类型2

4 range range类型用于包含一定范围内数字值的输入域,跟number一样,我们还可以对数值设置限定,range类型显示为滑动条用法如下: 上述代码使用了range类型输入框,为该类型设置了数值范围为...

老码农的一亩三分地
22分钟前
2
0
对比不同的数据库连接的异同

博主在学习和使用数据库连接时,遇到的问题, 这个几个数据库连接究竟有什么不同? 到底什么时候该使用哪个会更好一点? 带着这个问题我们先去了解常见的数据库连接 1. 常见的数据库连接有哪些?...

理性思考
24分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部