源码断点分析Spring的占位符(Placeholder)是怎么工作的

news2024/11/26 22:18:02

项目中经常需要使用到占位符来满足多环境不同配置信息的需求,比如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="myPropertyPlaceholderBean" class="com.example.demo1.PropertyPlaceholderBean">
        <property name="myPropertyName" value="${my.property.key}" />
    </bean>
    
</beans>

其中属性myPropertyName是带有’ ${}’ 符号,也就是占位符的变量,最终需要替换成具体的值,Spring会最终替换,那么它怎么做到的? 下面就通过打断点跟源码方式分析来分析说明。

还是以SpringBoot项目为例,在resources下定义结构如下:
在这里插入图片描述
以上结构是为了方便验证,随便定义的,大家可能有区别。
其中dev.properties定义两个key

test.env_var=123
my.property.key=justdoit

spring-bean1.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myPropertyPlaceholderBean" class="com.example.demo1.PropertyPlaceholderBean">
        <property name="myPropertyName" value="${my.property.key}" />
    </bean>

</beans>

spring-application.xml如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder  location="classpath:properties/dev.properties" ignore-unresolvable="true" />
    <import resource="spring-bean1.xml"/>
</beans>

Spring boot启动类定义

@SpringBootApplication
@ImportResource({"classpath:spring-application.xml"})
public class ClientServerApplication {
	public static void main(String[] args) {
		SpringApplication.run(ClientServerApplication.class, args);
	}
}

好了,下面开始分析整个过程…
首先从PropertySourcesPlaceholderConfigurer开始说,因为占位符默认就是由它来实现的。 进入其源码看到它是一个BeanFactoryPostProcessor 大家都知道,spring bean生命周期过程会执行所有BeanFactoryPostProcessor的postProcessBeanFactory方法,所以,肯定会进入到这个方法:
在这里插入图片描述
这里看到它尝试从两个地方去读取属性配置,一个是
以Environment为属性源的environmentProperties,另外一个就是通过loadProperties(Properties props)加载本地资源文件作为属性源的localProperties,我这个例子是第二种情况。

在这里插入图片描述
可以看到,已经加载到我上面配置的两个key-value
接着进入到下一步:
this.processProperties(beanFactory, (ConfigurablePropertyResolver)(new PropertySourcesPropertyResolver(this.propertySources)));

在这里插入图片描述
看到propertyResolver.setPlaceholderPrefix(this.placeholderPrefix)这些是设置缺省时,占位符的默认配置,即’${}’
其中注意一点,StringValueResolver valueResolver定义的是labmda表达式,后面会使用到。
接着下一步
在这里插入图片描述
上面这里是开始遍历所有的bean,替换其中包含占位符的bean的属性对象。
接着进入方法:
在这里插入图片描述遍历到我们自定义的bean,其中beanDefinition.getPropertyValues()是拿它的所有属性信息,如下图
在这里插入图片描述
遍历所有的属性,解析值,并且替换占位符
在这里插入图片描述
进入resolveValue方法,直接去到以下位置,因为属性类型是string嘛,所以直接跳到这里
在这里插入图片描述
可以看到我们bean中定义的占位符,接下来就是要替换它。接着看
在这里插入图片描述
发现此次是一个labmda表达式,就是上面提到的,所以执行回到上面的位置,
在这里插入图片描述
接着跟代码会进入到在这里插入图片描述
继续进入
parseStringValue
在这里插入图片描述
在这里插入图片描述
从propertySources里面去解析配置,疑问来了??这个对象什么时候放进去的,其实就是最开始提到的两个读取配置的地方,
一个是
以Environment为属性源的environmentProperties,另外一个就是通过loadProperties(Properties props)加载本地资源文件作为属性源的localProperties。
看以下代码就明白了,

if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
} else {
this.propertySources.addLast(localPropertySource);
}

解析完占位符得到值以后,出来回到resolveValue方法处,也就是很多if else的方法处,字符串位置
在这里插入图片描述
将属性值原本是${my.property.key}替换成justdoit

到此,对象PropertyPlaceholderBean定义的属性myPropertyName就被替换成具体的某个值了,这里也就是被替换成了 justdoit

总结:
基于Spring bean的生命周期,BeanFactoryPostProcessor执行方法postProcessBeanFactory,解析获取到属性源即environmentProperties以及localProperties两种,跟着解析占位符,然后得到具体的值,最后set进去替换占位符为具体的属性值。

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

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

相关文章

老师怎么发分班录取情况?这个快捷方式可以搞定

关于分班录取的发布方式&#xff0c;老师们可以参考下面的步骤来发布&#xff1a; 1. 班级通知&#xff1a;老师可以在学校的通知栏、公告栏或班级群里发布分班录取情况。在通知中&#xff0c;老师可以列出每个班级的学生名单&#xff0c;包括学生的姓名和分班结果。 2. 班级…

纽扣电池寿命和功率增强器

近日&#xff0c;基础半导体器件领域的高产能生产专家Nexperia&#xff08;安世半导体&#xff09;宣布推出NBM7100和NBM5100。这两款IC采用了具有突破意义的创新技术&#xff0c;是专为延长不可充电的典型纽扣锂电池寿命而设计的新型电池寿命增强器&#xff0c;相比于同类解决…

抖店商品详情API接口(关键词搜索商品列表API接口)

联盟商品和非联盟商品是抖店平台上的两种不同类型的商品。 联盟商品是指与抖店平台达成合作关系的商家提供的商品。这些商家通常是经过严格筛选和审核的合作伙伴&#xff0c;与抖店平台有合作协议&#xff0c;并享受一定的运营支持和优惠政策。联盟商品通常具有较高的品质和可…

【大数据】一些基本概念

一、数据库、数据仓库、数据湖 1.什么是数据库 (Database, DB) 数据库是指长期储存在计算机中的有组织的, 可共享的数据集合 就是存储数据的仓库 数据库有三个特点: 永久存储, 有组织, 可共享 数据库是一种结构化数据存储技术&#xff0c;用于存储和管理有组织的数据。数据库…

[HDLBits] Exams/m2014 q4a

Implement the following circuit: Note that this is a latch, so a Quartus warning about having inferred a latch is expected. module top_module (input d, input ena,output q);always(*) beginif(ena)qd;end endmodule

面试热题(合并K个升序链表)

给定一个链表数组&#xff0c;每个链表都已经按升序排列。 请将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#xff1a;[1,1,2,3,4,4,5,6] 解释&#xff1a;链表数组如下&#xff1a; [1->4->5,1…

Java多款线程池,总有一款适合你。

线程池的选择 一&#xff1a;故事背景二&#xff1a;线程池原理2.1 ThreadPoolExecutor的构造方法的七个参数2.1.1 必须参数2.1.2 可选参数 2.2 ThreadPoolExecutor的策略2.3 线程池主要任务处理流程2.4 ThreadPoolExecutor 如何做到线程复用 三&#xff1a;四种常见线程池3.1 …

企业数字化转型与股利分配(2007-2021年)

参照李滟&#xff08;2023&#xff09;的做法&#xff0c;本团队对来自西南大学学报&#xff08;社会科学版&#xff09;《企业数字化转型与股利分配》一文中的基准回归部分进行复刻。 企业数字化转型已成为我国经济增长的新引擎和新动力。为探究数字化转型对企业财务决策的影…

Spring 依赖注入和自动装配

DI&#xff08;依赖注入&#xff09; DI&#xff1a;Dependency Injection 共有三种方式 构造器注入 在前面IOC容器创建对象的方式中已经提到&#xff0c;无参构造器和有参构造器都可以。 Set方式注入&#xff08;重点&#xff09; 依赖注入&#xff1a;本质是Set注入 依赖…

【Linux】高级IO

目录 IO的基本概念 钓鱼五人组 五种IO模型 高级IO重要概念 同步通信 VS 异步通信 阻塞 VS 非阻塞 其他高级IO 阻塞IO 非阻塞IO IO的基本概念 什么是IO&#xff1f; I/O&#xff08;input/output&#xff09;也就是输入和输出&#xff0c;在著名的冯诺依曼体系结构当中…

LinuxC编程——进程间通信(二)(信号、共享内存)

目录 一、信号1.1 概念1.2 信号的响应方式⭐⭐⭐1.3 几种常见的信号1.4 函数练习 二、共享内存2.1 共享内存的特点2.2 共享内存创建步骤⭐⭐2.3 共享内存创建所需函数 信号主要用来通知进程异步事件的发生。最初信号设计的目的是为了处理错误&#xff0c;它们也用来作为最基本的…

【EI/SCOPUS检索】第二届能源与动力工程国际学术会议(EPE 2023)

第二届能源与动力工程国际学术会议&#xff08;EPE 2023&#xff09; 2023 2nd International Conference on Energy and Power Engineering 能源是人类社会发展的重要推动力量。如何安全、清洁、高效地存储、转化和利用能源&#xff0c;实现人类可持续发展&#xff0c;一直…

比例方向阀控制多功能放大器

适用于控制无电位置反馈的三位四通比例方向阀&#xff0c;两路独立工作的比例放大器&#xff0c;可组合成并联工作方式&#xff0c;0到10V输入接口&#xff0c;可切换为0(4)到20mA输入&#xff0c;工作电压24VDC&#xff0c;允许工作温度范围0~45℃&#xff0c;放大器只有在断电…

spring security实践-全套代码

贴一套完整代码 电脑文件都被加密了&#xff0c;无法上传git&#xff0c;留一套在此&#xff0c;日后方便。 整个学习过程参考的spring security 1. 项目目录结构 2.初始化数据库 CREATE TABLE sys_user (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 主键,user_name VAR…

你确定你的 REST API 真的符合 REST 规范?

RESTful API 的存在是 web 开发历史上的一个里程碑。在本文中&#xff0c;我将和你探讨几种节省 REST API 开发时间的方法&#xff0c;并给出相关的 Node.js 示例。 什么是 RESTful API 首先&#xff0c;想问一个问题&#xff0c;你的项目里真的有真正的 RESTful API 吗&…

【构造】CF1734 E

Problem - 1734E - Codeforces 题意&#xff1a; 思路&#xff1a; Code&#xff1a; #include <bits/stdc.h>#define int long longusing i64 long long;constexpr int N 1e3 10; constexpr int mod 1e9 7;int n; int a[N], ans[N][N];void solve() {std::cin &g…

动力节点零基础Java学习教程,从入门到进阶,轻松掌握Java开发技能

Java是一门应用非常广泛的编程语言&#xff0c;对于零基础自学Java来说&#xff0c;Java的学习过程可能会有一些困难&#xff0c;但只要掌握了相关的基础知识和技能&#xff0c;不断地实践和总结&#xff0c;就能真正掌握Java编程。 动力节点老杜的Java经典之作&#xff0c;哔站…

Jenkins构建自由风格项目发布jar到服务器

前面的文章有介绍 docker安装jenkins 和 dockerjenkins发布spring项目&#xff1b;这里就不做过多的介绍&#xff0c;直接说明构建步骤。 1、选择构建一个自由风格的项目 2、 选择丢弃旧的构建 3、配置Git信息 4、构建触发器 和 构建环境可以直接跳过 5、直接来到Build Step…

技术广度必备——高并发设计之分布式锁的实现方式

文章目录 问题背景前言实现基于MySQL实现唯一索引乐观锁悲观锁 基于Redis基于Zookeeper原理使用Curator框架实现ZK分布式锁缺点 问题背景 研究有哪几种方案可以实现分布式锁&#xff0c;技术选型的场景下能用到。 前言 本文参考过的文章有分布式锁的几种实现方式方式大致分为3种…

蓝帽杯 取证2022

网站取证 网站取证_1 下载附件 并解压 得到了一个文件以及一个压缩包 解压压缩包 用火绒查病毒 发现后门 打开文件路径之后 发现了一句话木马 解出flag 网站取证_2 让找数据库链接的明文密码 打开www文件找找 查看数据库配置文件/application/database.php&#xff08;CodeI…