Spring @Conditional注解源码分析

概述

Import注解Java doc

指示要导入的一个或多个组件类,通常为@Configuration类。

提供与Spring XML中的<import/>元素等效的功能。允许导入@Configuration类、ImportSelector和ImportBeanDefinitionRegistrar实现,以及常规组件类(自4.2起;类似于AnnotationConfigApplicationContext.register(java.lang.Class<?> …))。

导入的@Configuration类中声明的@Bean定义应该使用@Autowired注入来访问。要么bean本身可以是自动装配的,要么声明bean的配置类实例可以是自动装配的。

可以在类级别和MetaAnnotation声明。

如果是XML或其他非@Configuration类需要导入@Bean定义资源,可以使用@ImportResource注解。

这个注解需要传一个Class<?>[]参数:@Configuration、ImportSelector、ImportBeanDefinitionRegistrar或者其他常规组件类。

ImportSelector接口

实现类需要根据给定的Select条件(通常是一个或多个Annotation Attribute)确定应导入哪些@Configuration类。

实现类可以实现以下Aware接口,在调用selectImports之前,将会调用它们各自的Aware逻辑:

  • EnvironmentAware
  • BeanFactoryAware
  • BeanClassLoaderAware
  • ResourceLoaderAware

或者,该类可以为单个构造函数提供一个或多个以下支持的参数类型:

  • Environment
  • BeanFactory
  • ClassLoader
  • ResourceLoader

ImportSelector实现类通常以与常规@Import注解相同的方式进行处理,但是,也可以推迟Selector导入,即直到处理完所有@Configuration类,DeferredImportSelector接口可以实现这个功能。

该接口提供一个方法:

// Select and return the names of which class(es) should be imported based on 
// the AnnotationMetadata of the importing @Configuration class.
String[] selectImports(AnnotationMetadata importingClassMetadata)

DeferredImportSelector接口

继承ImportSelector接口。

ImportSelector的变体,在处理完所有@Configuration Bean之后运行。当导入为@Conditional时,这种类型的选择器可能特别有用。

实现类可以扩展Ordered接口或使用Order注释来指示相对于其他DeferredImportSelector的优先级。

getImportGroup方法需要返回一个DeferredImportSelector.Group的实现类Class对象,用于确定哪些类需要导入。

Group接口提供两个方法:

// Process the AnnotationMetadata of the importing @Configuration class 
// using the specified DeferredImportSelector.
process(AnnotationMetadata metadata, DeferredImportSelector selector)

// Return the entries of which class(es) should be imported for this group.
selectImports()

ImportBeanDefinitionRegistrar接口

该接口的实现类会在处理@Configuration类时向容器注册额外的Bean定义,当需要在Bean Definition阶段进行一些操作时,这个接口的实现类就很有用。

与@Configuration和ImportSelector一起使用,可以用ImportSelector的返回值向@Import注解提供此接口的实现类。

// Register bean definitions as necessary based on the given annotation metadata 
// of the importing @Configuration class.
default void registerBeanDefinitions(
    	AnnotationMetadata importingClassMetadata,
    	BeanDefinitionRegistry registry,
		BeanNameGenerator importBeanNameGenerator) {
	registerBeanDefinitions(importingClassMetadata, registry);
}

// Register bean definitions as necessary based on the given annotation metadata 
// of the importing @Configuration class.
default void registerBeanDefinitions(
    	AnnotationMetadata importingClassMetadata,
    	BeanDefinitionRegistry registry) {
}

基本流程

ConfigurationClassPostProcessor类

在@Bean注解解析中,介绍了AnnotationConfigApplicationContext创建时会注册几个注解驱动处理器,其中就包括ConfigurationClassPostProcessor类。

他是BeanDefinitionRegistryPostProcessor实现,用于解析@Configuration类,他是按优先级排序的,因为在@Configuration类中声明的任何Bean方法都必须在任何其他BeanFactoryPostProcessor执行之前注册其对应的BeanDefinition。

他在invokeBeanFactoryPostProcessors(beanFactory)阶段被调用,通过postProcessBeanDefinitionRegistry(registry)方法解析@Configuration类。

这个过程的源码在@Bean注解解析中记录过,此处不再记录。

调用到processConfigBeanDefinitions(registry)方法,其中有一段使用ConfigurationClassParser解析@Configuration类的代码,如下:

// 1. Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
		this.metadataReaderFactory, this.problemReporter, this.environment,
		this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

// 此处是一个do while循环
// 因为解析一遍之后,容器里面可能会有新的被注入的@Configuration Class定义,需要进一步解析
// 比如@Import注解就有可能注入新的@Configuration Class定义
do {
	parser.parse(candidates);
	parser.validate();

	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
	configClasses.removeAll(alreadyParsed);

	// 2. Read the model and create bean definitions based on its content
	this.reader.loadBeanDefinitions(configClasses);

	// ...
} while (!candidates.isEmpty());

// ...

以上代码做了两件事:

  • Parse @Configuration class
  • 解析ConfigurationClass集注册BeanDefinition

Parse @Configuration class

public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			if (bd instanceof AnnotatedBeanDefinition) {
				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("", ex);
		}
	}

	// DeferredImportSelector处理
	this.deferredImportSelectorHandler.process();
}

三个分支最终都会调用到processConfigurationClass方法,该方法会递归的分析一个ConfigurationClass及其父类,核心源码在以下这几行:

// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
	// 分析ConfigurationClass
	sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
} while (sourceClass != null);

this.configurationClasses.put(configClass, configClass);

doProcessConfigurationClass方法:

  1. 递归解析内部类

  2. 解析@PropertySource注解

  3. 解析@ComponentScan注解

  4. 解析@Import注解

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    
  5. 解析@ImportResource注解

  6. 解析@Bean注解

  7. 获取父类用于递归解析

processImports方法解析@Import注解

该方法需要以下参数:

  1. ConfigurationClass configClass - 当前分析的@Configuration类
  2. SourceClass currentSourceClass - 这个参数与configClass不一样,configClass始终不变,是当前@Configuration类,而这个参数会随着递归解析父类而改变
  3. Collection<SourceClass> importCandidates - @Import注解导入类
  4. Predicate<String> exclusionFilter - 会过滤掉java.lang.annotation和org.springframework.stereotype下面的类
  5. boolean checkForCircularImports - 检测循环Import

代码:

for (SourceClass candidate : importCandidates) {
	if (candidate.isAssignable(ImportSelector.class)) {
		// 这个分支支持ImportSelector

		// Candidate class is an ImportSelector -> delegate to it to determine imports
		Class<?> candidateClass = candidate.loadClass();
		ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
				this.environment, this.resourceLoader, this.registry);
		Predicate<String> selectorFilter = selector.getExclusionFilter();
		if (selectorFilter != null) {
			exclusionFilter = exclusionFilter.or(selectorFilter);
		}
		if (selector instanceof DeferredImportSelector) {
			// 这个分支支持DeferredImportSelector

			this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
		} else {
			String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
			Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
			processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
		}
	} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
		// 这个分支支持ImportBeanDefinitionRegistrar

		// Candidate class is an ImportBeanDefinitionRegistrar ->
		// delegate to it to register additional bean definitions
		Class<?> candidateClass = candidate.loadClass();
		ImportBeanDefinitionRegistrar registrar =
				ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
						this.environment, this.resourceLoader, this.registry);
		configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
	} else {
		// 这个分支支持@Configuration class

		// 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), exclusionFilter);
	}
}

ImportSelector支持

以下的代码片段是解析ImportSelector的逻辑:

// 调用selectImports方法获取导入的组件集
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 这里又递归调用了processImports方法
// 所以selectImports返回的类型可以是:普通@Configuration类、ImportSelector或者ImportBeanDefinitionRegistrar
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);

DeferredImportSelector支持

if (selector instanceof DeferredImportSelector) {
	this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

/**
 * Handle the specified DeferredImportSelector.
 * If deferred import selectors are being collected, this registers this instance to the list.
 * If they are being processed,
 * the DeferredImportSelector is also processed immediately according to its DeferredImportSelector.Group.
 */
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
	DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
	if (this.deferredImportSelectors == null) {
		DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
		handler.register(holder);
		handler.processGroupImports();
	} else {
        // If deferred import selectors are being collected, this registers this instance to the list.
        // 等待解析了所有的@Configuration类之后再处理DeferredImportSelector
		this.deferredImportSelectors.add(holder);
	}
}

process核心逻辑:

public void processGroupImports() {
	for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
		Predicate<String> exclusionFilter = grouping.getCandidateFilter();
		grouping.getImports().forEach(entry -> {
			ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
			try {
				processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
						Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
						exclusionFilter, false);
			} catch (BeanDefinitionStoreException ex) {
				throw ex;
			} catch (Throwable ex) {
				throw new BeanDefinitionStoreException("", ex);
			}
		});
	}
}

// getImports方法
public Iterable<Group.Entry> getImports() {
	for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
		this.group.process(deferredImport.getConfigurationClass().getMetadata(),
				deferredImport.getImportSelector());
	}
	return this.group.selectImports();
}

ImportBeanDefinitionRegistrar支持

// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
// 实例化ImportBeanDefinitionRegistrar对象
ImportBeanDefinitionRegistrar registrar =
		ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
				this.environment, this.resourceLoader, this.registry);

// 添加到ConfigurationClass的importBeanDefinitionRegistrars集合
// 等待后续loadBeanDefinitions时会调用registerBeanDefinitions方法让开发者注册BeanDefinition
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

普通@Configuration类

// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
		currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());

// 调用processConfigurationClass方法解析@Configuration class
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/9818.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023/4/6总结

题解 Problem - A - Codeforces 1.这道题很简单&#xff0c;找出将当前数字放入字符串的最大值。 2.分情况讨论&#xff0c;有俩种情况&#xff0c;一种是大于等于数字d&#xff0c;那么这个数字d需要插入到最后字符串的位置。否则这个数字需要插入到第一次比它小的位置。 …

2023年4月的编程语言排行榜,有你中意的开发语言吗?

编程世界变幻莫测&#xff0c;编程语言也是层出不穷&#xff0c;每隔一段时间就有新的风口出现。2023年的风口非人工智能莫属&#xff0c;人工智能领域中不可获取的编程语言就是Python&#xff0c;作为在算法、数据方面有独特优势的编程语言&#xff0c;从去年开始就展现了它不…

算法学习|动态规划 LeetCode 392.判断子序列 、115.不同的子序列

动态规划一、判断子序列思路实现代码二、不同的子序列思路实现代码(还是蛮开心的&#xff09; 一、判断子序列 给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相…

腾讯云8核16G18M轻量服务器CPU带宽流量性能测评

腾讯云轻量应用服务器8核16G18M带宽&#xff0c;18M公网带宽下载速度峰值可达2304KB/秒&#xff0c;相当于2.25M/s&#xff0c;系统盘为270GB SSD盘&#xff0c;3500GB月流量&#xff0c;折合每天116GB流量。腾讯云百科分享腾讯云轻量服务器8核16G18M配置、CPU型号、公网带宽月…

【Pytorch】搭建网络模型的实战

【Pytorch】搭建网络模型的实战CIFAR10 model structure搭建网络使用Sequential进行搭建网络模型使用tensorboard查看网络结构对CIFAR10数据集进行分类&#xff0c;根据图片内容识别这是哪一类 CIFAR10 model structure 输入input:3通道的32 x 32 图片卷积操作的通道数不变 那…

应用信息资源管理(张士玉、董焱)——第一章 总论

第一章 总论 1.1 信息社会 1.1.1 信息社会的形成 信息社会是指以信息技术为基础&#xff0c;信息产业为支撑&#xff0c;信息化为主要特征的社会形态。其形成经历了以下几个阶段&#xff1a; 信息化初期&#xff1a;20世纪60年代至70年代&#xff0c;电子计算机的出现和发展…

人工智能会取代人工翻译吗?

当今社会正处于语言和技术高速发展的阶段&#xff0c;因此语言和技术的碰撞是不可避免的——甚至有些人说这种碰撞已经发生了&#xff0c;我们只是在等待尘埃落定。数字化、物联网、人工智能和机器学习&#xff0c;以及更进一步——智能手机、语音识别&#xff0c;以及互联网和…

机器视觉检测技术在工业零部件的应用

众所周知&#xff0c;在工业生产中&#xff0c;传统的检测技术需要大量的检测工作者&#xff0c;不仅影响生产效率&#xff0c;而且带来不可靠的因素。 视觉检测技术克服了传统检测技术的缺点&#xff0c;确保了检测的安全性。 可靠性和自动化程度高&#xff0c;已成为当前检测…

第六章 信息资源安全管理

信息资源安全管理内涵 信息在开发利用过程中面临的问题&#xff1a; 可用性&#xff1b;合法用户对信息的使用不会被不正当拒绝保密性也称机密性&#xff1b;保证机密信息不被窃取&#xff0c;或窃取者不能了解信息的真实含义认证性也称真实性&#xff1b;对信息的来源进行判断…

zabbix创建自定义监控模板

目录 第一章先行配置zabbix 第二章配置自定义 2.1.案列&#xff1a;自定义监控客户端服务器登录的人数需求&#xff1a;限制登录人数不超过 3 个&#xff0c;超过 3 个就发出报警信息 2.2.在 Web 页面创建自定义监控项模板 2.3.zabbix 自动发现与自动注册 总结 自定义监控…

Chat-GLM 详细部署(GPU显存>=12GB)

建议配置: ( Windows OS 11 部署 )CPU-i7 13700F ~ 13700KF RAM: 16GB DDR4 GPU: RTX3080(12G) 安装 conda: 1. 下载安装 miniconda3 &#xff1a; https://docs.conda.io/en/latest/miniconda.html conda是一个包和环境管理工具&#xff0c;它不仅能管理包&#xff0c;还能隔…

龙蜥 Anolis 8.x + Vmware的安装与网络配置 CentOS8 网络配置详细教程

前言 配置和安装可以看下面这两篇文章的 写的很详细https://cnxiaobai.com/articles/2021/04/21/1619011285612.htmlhttps://cnxiaobai.com/articles/2021/10/21/1634800698273.html#b3_solo_h3_1网络配置方面有不同的方面 我在下面进行了修改这个操作系统 很少资源 弄了好久才…

C++轻量级Web服务器TinyWebServer源码分析之threadpool篇

文章目录threadpool线程池篇简介一、线程池的创建与回收二、向请求队列添加请求任务三、worker函数内部访问run函数&#xff0c;完成线程处理四、run函数执行任务原文链接threadpool线程池篇简介 空间换时间,浪费服务器的硬件资源,换取运行效率. 池是一组资源的集合,这组资源…

天气预报查询 API + AI 等于王炸(一大波你未曾设想的天气预报查询 API 应用场景更新了)

前言 近年来&#xff0c;随着信息化进程的不断深入&#xff0c;人们对于信息的获取和处理需求越来越高。而其中&#xff0c;天气查询API是一个非常重要的服务&#xff0c;它能够帮助人们快速获取所在位置的天气情况&#xff0c;同时也为各类应用提供了必要的气象数据支持。 本…

vue3笔记

目录 1.vue3带来了什么 1.1源码的升级 1.2拥抱TypeScript 1.3新的特性 2.创建Vue3.0工程 2.1使用vue-cli创建 2.2使用vite创建 3.创建vue3.0工程 3.1 main.js 3.2 App.vue 4.安装开发者工具 5.常用的 Composition API 5.1拉开序幕的setup 6.ref函数 7.reactive函…

全国青少年软件编程(Scratch)等级考试二级考试真题2023年3月——持续更新.....

一、单选题(共25题,共50分) 1. 小猫的程序如图所示,积木块的颜色与球的颜色一致。点击绿旗执行程序后,下列说法正确的是?( ) A.小猫一直在左右移动,嘴里一直说着“抓到了”。 B.小猫会碰到球,然后停止。 C.小猫一直在左右移动,嘴里一直说着“别跑” D.小猫会碰到球,…

全新适配鸿蒙生态,Cocos引擎助力3D应用开发

一、适配HarmonyOS背景 HarmonyOS 3.1版本自发布以来&#xff0c;备受广大开发者的好评&#xff0c;同时也吸引了鸿蒙生态众多伙伴的青睐。 鸿蒙生态所强调的智慧全场景、多端联动与跨设备流转等能力&#xff0c;与Cocos所具有的跨平台、低功耗、高性能三大核心特点不谋而合。C…

【Python】字符串 ④ ( Python 浮点数精度控制 | 控制数字的宽度和精度 )

文章目录一、Python 字符串格式化1、浮点数精度问题2、浮点数精度控制一、Python 字符串格式化 1、浮点数精度问题 在上一篇博客 【Python】字符串 ③ ( Python 字符串格式化 | 单个占位符 | 多个占位符 | 不同类型的占位符 ) 中 , 拼接字符串中 , float 浮点类型出现如下情况 …

国内怎么玩chatGPT-chatGPT中文版入口

ChatGPT国内可用版 目前&#xff0c;国内有一些可用的ChatGPT模型和平台&#xff0c;可以方便用户使用。以下是一些代表性的中文ChatGPT模型和平台&#xff1a; THU Transformer: 清华大学自然语言处理实验室开发的中文自然语言处理模型&#xff0c;基于GPT模型架构进行研发&a…

WPS卡慢解决方法

WPS卡顿&#xff0c;很慢怎么解决&#xff1f; keywords:wps很卡很慢怎么办、wps很卡什么原因、wps很卡怎么解决、wps很占内存吗、wps很慢、wps打开文件很慢很卡怎么办、wps打开文件很慢很卡 说起来真的很搞笑&#xff0c;你可以试试把下面这个功能给勾选&#xff0c;速度X10…
最新文章