Sping源码(七)—ConfigurationClassPostProcessor ——@PropertySources解析

news2024/11/30 2:41:03

序言

先来简单回顾一下ConfigurationClassPostProcessor大致的一个处理流程,再来详细的讲解@PropertySources注解的处理逻辑。
详细的步骤可参考ConfigurationClassPostProcessor这篇帖子。

流程图
从获取所有BeanDefinition -> 过滤、赋值、遍历 -> 解析 -> 优先递归处理内部类这一系列操作后。
来到了最后一步,处理@Bean、@Configuration、@Component、@ComponentScan、@PropertySource等注解。
按照代码的执行顺序,首先介绍@PropertySource注解的执行原理。
在这里插入图片描述

@PropertySource

测试类
创建类MyService并添加@Configuration和@PropertySource。
其中@Configuration继承了@Component , 而@PropertySource引入了myconfig.properties文件。

@Configuration
@PropertySource({"classpath:myconfig.properties"})
public class MyService{
}

Other同样加载了myconfig.properties文件

@Component
@PropertySource({"classpath:myconfig.properties"})
public class Other {

}

myconfig.properties
文件中简单的设置一个name属性即可。

myconfig.name=lisi

流程图
在这里插入图片描述

源码片段
来看看加载后解析@PropertySource时做了什么。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		//如果是@Component注解,则优先递归处理内部类
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass, filter);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

	//省略部分源码 。。。
	}

获取注解中属性

获取注解中我们定义的name、encoding、value等属性值,根据我们定义的java类中只有value有具体的属性值。
获取value属性后转换成Resource对象,并再次封装成ResourcePropertySource对象用来存储properties文件属性。

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
		//获取name属性值
		String name = propertySource.getString("name");
		if (!StringUtils.hasLength(name)) {
			name = null;
		}
		//获取encoding属性值
		String encoding = propertySource.getString("encoding");
		if (!StringUtils.hasLength(encoding)) {
			encoding = null;
		}
		//获取value属性值
		String[] locations = propertySource.getStringArray("value");
		Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
		boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

		Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
		PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
				DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
		//遍历location
		for (String location : locations) {
			try {
				String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
				Resource resource = this.resourceLoader.getResource(resolvedLocation);
				addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
			}
		
		//省略catch捕获异常部分源码。。
		}
	}
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
		return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
	}

其中遍历location部分源码是否有些熟悉?
同样是String -> Resource的转换。refresh()主流程方法中将xml文件也是将String[] -> String -> Resource[] -> Resource。

首次加载

将文件封装成ResourcePropertySource对象后,如果该文件未被加载过则添加到propertySources属性尾端,否则封装成CompositePropertySource对象。
值得注意的是,目前只是对文件进行加载,没有对文件中字段属性做特别处理。

addPropertySource
我们第一次加载处理MyService类中@PropertySource注解时,propertySourceNames属性的size = 0, 所以会直接添加到propertySources的尾端。

private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		//如果资源文件名已经存在,则进行扩展
		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			//获取已经存在的资源文件
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				//获取扩展的资源文件
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				//如果资源文件是CompositePropertySource,则添加到CompositePropertySource中
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					//如果资源文件不是CompositePropertySource,则创建CompositePropertySource
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		// 如果没有已处理的属性源,将新属性源添加到末尾;如果有,则在最后一个处理的属性源之前添加
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}

因为我们只设置了@PropertySource中的value属性,所以resourceName为null。

public ResourcePropertySource withResourceName() {
		if (this.resourceName == null) {
			return this;
		}
		return new ResourcePropertySource(this.resourceName, null, this.source);
	}

而最后添加的propertySources属性,我们debug看一下它里面都有什么。
在这里插入图片描述
可以看到,我们的myconfig.properties文件已经加到了里面,并且source中有我们设置的myconfig.name字段。
而前两个systemProperties和systemEnvironment属性在之前帖子中也有讲到。是我们StandardEnvironment对象创建时获取到的系统变量。

在父类AbstractEnvironment构造方法中 进行调用。

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
	}

public class StandardEnvironment extends AbstractEnvironment {

	/** System environment property source name: {@value}. */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value}. */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";

	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(
				new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(
				new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

}

再次加载

MyService类对应的@PropertySource已经处理完成,再来看看Other类中的@PropertySource注解加载时都做了什么?

源码片段
依然是addPropertySource()方法没有变,此时要加载的资源文件都是myconfig.properties。所以propertySourceNames属性不为null。
代码逻辑会走if判断并将之前加载过的资源文件(existing)和新封装的资源文件propertySource(myconfig.properties)封装到CompositePropertySource,并根据name计算索引替换propertySources原位置的propertySource。

private void addPropertySource(PropertySource<?> propertySource) {
		String name = propertySource.getName();
		MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();

		//如果资源文件名已经存在,则进行扩展
		if (this.propertySourceNames.contains(name)) {
			// We've already added a version, we need to extend it
			//获取已经存在的资源文件
			PropertySource<?> existing = propertySources.get(name);
			if (existing != null) {
				//获取扩展的资源文件
				PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
						((ResourcePropertySource) propertySource).withResourceName() : propertySource);
				//如果资源文件是CompositePropertySource,则添加到CompositePropertySource中
				if (existing instanceof CompositePropertySource) {
					((CompositePropertySource) existing).addFirstPropertySource(newSource);
				}
				else {
					//如果资源文件不是CompositePropertySource,则创建CompositePropertySource
					if (existing instanceof ResourcePropertySource) {
						existing = ((ResourcePropertySource) existing).withResourceName();
					}
					CompositePropertySource composite = new CompositePropertySource(name);
					composite.addPropertySource(newSource);
					composite.addPropertySource(existing);
					propertySources.replace(name, composite);
				}
				return;
			}
		}

		// 如果没有已处理的属性源,将新属性源添加到末尾;如果有,则在最后一个处理的属性源之前添加
		if (this.propertySourceNames.isEmpty()) {
			propertySources.addLast(propertySource);
		}
		else {
			String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
			propertySources.addBefore(firstProcessed, propertySource);
		}
		this.propertySourceNames.add(name);
	}

以上就是@PropertySource注解解析的全过程。需要注意的是!!!
此时仅仅是文件的加载,并未对文件中属性、字段做任何逻辑处理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用的简单友好的工单系统(免费)- WGCAT

最近在项目中&#xff0c;有工单系统的需求场景&#xff0c;所以想寻找一款轻量简单的运维工单软件&#xff0c;主要用来记录和处理工作中的一些故障、维护&#xff0c;主要用来记录设备的维护状态&#xff0c;包括服务器、主机、交换机那些 WGCAT&#xff0c;是一款简单轻量的…

2024中国(重庆)人工智能展览会8月举办

2024中国(重庆)人工智能展览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 【报名I59交易会 2351交易会 9466】 展会背景&#xff1a; 2024中国航空科普大会暨第八届全国青少年无人机大赛在…

macOS12安装 php8.1和apache

1. 安装php 8.1 macOS12不再自带php brew tap shivammathur/php 查看可安装版本 brew search php 安装指定版本 brew install php8.1 环境配置 vim ~/.zshrc export PATH"/usr/local/opt/php8.1/bin:$PATH" export PATH"/usr/local/opt/php8.1/sbin:$PAT…

Git Bash和Git GUI设置中文的方法

0 前言 Git是一个分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。一般默认语言为英文&#xff0c;本文介绍修改Git Bash和Git GUI语言为中文的方法。 1 Git Bash设置中文方法 &#xff08;1&#xff09;鼠标右键&#xff0c;单击“Git B…

每日两题 / 108. 将有序数组转换为二叉搜索树 543. 二叉树的直径(LeetCode热题100)

108. 将有序数组转换为二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 每次将数组对半分&#xff0c;数组的中点作为树的节点 先选择整个数组的中点作为根节点&#xff0c;然后选择对半分后的两个子数组的中点作为根节点的左右节点… /*** Definition for a binary tre…

【操作系统期末速成】​内存管理|内存的装入模块在装入内存的方式|分配管理方式|页面置换算法|页面置换

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;操作系统&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到…

Django开发实战之定制管理后台界面及知识梳理(上)

不知道不觉写博客已经半个月了&#xff0c;涨了164个粉丝&#xff0c;在一边分享笔记的过程&#xff0c;一边收获粉丝&#xff0c;感觉很开心也很幸福&#xff0c;希望看我博客的小伙伴都能有所收获&#xff0c;大家共同成长进步&#xff0c;好拉&#xff0c;话不多说&#xff…

hcip实验6:BGP综合实验

实验拓扑&#xff1a; 实验配置&#xff1a; ip地址配置&#xff1a; #R1 [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [R1-GigabitEthernet0/0/0]int l0 [R1-LoopBack0]ip add 172.16.0.1 32 [R1-LoopBack0]int l1 [R1-LoopBack1]ip add 192.168.1.1 24#R2…

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测 目录 回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现GA-LSSVM遗传算法优化最小…

【RAG论文】RAG中半结构化数据的解析和向量化方法

论文简介 论文题目&#xff1a; 《A Method for Parsing and Vectorization of Semi-structured Data used in Retrieval Augmented Generation》 论文链接&#xff1a; https://arxiv.org/abs/2405.03989 代码: https://github.com/linancn/TianGong-AI-Unstructure/tree/m…

阮怀俊参与五龙乡黄沙村村企联办“强村公司”

为走好海岛县高质量发展共同富裕特色之路&#xff0c;探索村级集体经济发展新路径、扶持新模式、运行新机制&#xff0c;嵊泗县五龙乡黄沙村股份经济合作社与杭州山舍乡建乡村产业发展有限责任公司联办成“强村公司”。 创始人阮怀俊表示&#xff0c;双方就融合乡域发展和文旅产…

Linux 操作系统MySQL 数据库1

1.MySQL 数据库 数据库是“按照数据结构来组织、 存储和管理数据的仓库”。 是一个长期存储在计算机内的、 有组织的、 可共享的、 统一管理的大量数据的集合。 它的存储空间很大&#xff0c; 可以存放百万条、 千万条、 上亿条数据。 但是数据库并不是随意地将数据进行…

python如何单步调试

Python怎么单步调试&#xff1f;下面给大家介绍一下单步调试&#xff1a; 方法一&#xff1a;执行 python -m pdb myscript.py (Pdb) 会自己主动停在第一行。等待调试&#xff0c;这时你能够看看帮助。 方法二&#xff1a;在所调试程序的开头中&#xff1a;import pdb 并在你…

MySQL中逗号分隔字段查询方法

MySQL中逗号分隔字段查询 select * FROM th_work_gand_up where FIND_IN_SET(11,lane_code) ; select * from th_work_gand_up where lane_code regexp (^|,)(11|1)(,|$);

金融业开源软件应用 管理指南

金融业开源软件应用 管理指南 1 范围 本文件提供了金融机构在应用开源软件时的全流程管理指南&#xff0c;对开源软件的使用和管理提供了配套 组织架构、配套管理规章制度、生命周期流程管理、风险管理、存量管理、工具化管理等方面的指导。 本文件适用于金融机构规范自身对开…

HDFS- DataNode磁盘扩缩容

HDFS- DataNode磁盘扩缩容 背景: 缩减/增加节点磁盘 方案介绍: 采用hdfs dfsadmin -reconfig 动态刷新配置实现,不停服扩缩容。 注意事项: 请在进行缩容之前,务必了解实际的数据量,并确保磁盘有足够的空间来容纳这些数据。还需要考虑未来的使用需求,要预留一定数量的空间…

Jboss 反序列化 CVE-2017-12149

一、漏洞简介 JBoss是一个管理EJB的容器和服务器&#xff0c;支持EJB 1.1、EJB 2.0和EJB3的规范。在/invoker/readonly路径下&#xff0c;攻击者可以构造序列化代码传入服务器进行反序列化,由于没有对反序列化操作进行任何检测&#xff0c;导致攻击者可以执行任意代码。 而jbo…

chmod -R 777 / 抢救,看这篇就够了

chmod -R 777抢救全过程记录 背景 在两台Ubuntu 20.04的服务器上错误执行了chmod -R 777 /命令&#xff0c;结果非常酸爽&#xff0c;sudo权限失效&#xff0c;而且ssh也没有用了。在经过了10多个小时的踩坑以后最后在不重装系统的情况下解决了问题&#xff0c;以下记录只记录…

C++ requires关键字简介

requires 是 C20 中引入的一个新关键字&#xff0c;用于在函数模板或类模板中声明所需的一组语义要求&#xff0c;它可以用来限制模板参数&#xff0c;类似于 typename 和 class 关键字。 requires关键字常与type_traits头文件下类型检查函数匹配使用&#xff0c;当requires后…

Selenium操作对象的方法汇总(如click/clear/submit/sendKeys/getText/getSize等)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…