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

序言

先来简单回顾一下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 firstprocessMemberClasses(configClass, sourceClass, filter);}// Process any @PropertySource annotationsfor (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));//遍历locationfor (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,则创建CompositePropertySourceif (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";@Overrideprotected 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,则创建CompositePropertySourceif (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.xdnf.cn/news/1420765.html

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

相关文章

CCleaner系统优化与隐私保护工具,中文绿色便携版 v6.23.11010

01 软件介绍 CCleaner 是一款高级的系统优化工具&#xff0c;其设计宗旨在于彻底清理 Windows 操作系统中积累的无用文件和冗余的注册表项。此举旨在显著提升计算机的运行效率并回收磁盘空间。该软件拥有高效的能力&#xff0c;可以清除包括临时文件、浏览器缓存及其历史记录在…

windows系统安装Ubuntu子系统

安装前先在 控制面板 中打开 程序与功能选项 &#xff0c;点击 启用或关闭Windows功能&#xff1a; 勾选 适用于 Linux的Windows子系统 和 虚拟机平台 、 Hyper-v 。 重启电脑后再 Microsoft Store Windows应用商店 中下载合适的Ubuntu版本。 运行Ubuntu程序&#xff0c;如出现…

如何在huggingface上申请下载使用llama2/3模型

1. 在对应模型的huggingface页面上提交申请 搜索对应的模型型号 登录huggingface&#xff0c;在模型详情页面上&#xff0c;找到这个表单&#xff0c;填写内容&#xff0c;提交申请。需要使用梯子&#xff0c;country填写梯子的位置吧(比如美国&#xff09; 等待一小时左右…

四川景源畅信:小白做抖音电商怎么样?

在数字时代&#xff0c;抖音已成为一个不可忽视的电商平台。对于初入行的小白来说&#xff0c;涉足抖音电商似乎既充满机遇又伴随着挑战。要判断小白做抖音电商的可行性&#xff0c;我们不妨从几个关键方面进行深入探讨。 一、市场趋势与流量获取 抖音作为新媒体的代表之一&…

web移动端调试——vconsole

因为项目是嵌套app内的H5界面&#xff0c;没办法直接查看控制台&#xff0c;所以使用了这个插件vconsole 一、使用 cdn 方式引入 <script src"https://cdn.bootcss.com/vConsole/3.3.4/vconsole.min.js"></script> <script> // 初始化 var vConso…

PyTorch的卷积和池化

卷积计算 input 表示输入的图像filter 表示卷积核, 也叫做滤波器input 经过 filter 的得到输出为最右侧的图像&#xff0c;该图叫做特征图 卷积的计算是将卷积核放入左上角&#xff0c;在局部区域间做点积&#xff0c;然后将卷积核在Input上面依次从左向右&#xff0c;从上到下…

高校课程评价|基于SSM+vue的高校课程评价系统的设计与实现(源码+数据库+文档)

高校课程评价系统 目录 基于SSM&#xff0b;vue的高校课程评价系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1管理员功能模块 2学生功能 3教师功能 4专家功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&…

Linux FT260驱动内核学习笔记

目录 1. 安装ft260驱动 2. 编译ft260源码 3. 通过sysfs配置ft260设备 3.1 多功能GPIO配置 3.2 控制GPIO 3.3 配置i2c总线频率 4. UART 5. 使用i2c-tools交互I2C设备 5.1 安装i2c-tools 5.2 探测I2C设备 5.3 读取所有寄存器数据 5.4 读取和写入 5.5 16位地址的读写…

web前端之纯CSS实现简单酷炫的照片墙效果、排除元素的伪类、scale

MENU 效果htmlstylescale:not() 效果 html <div class"container"><div class"box"><img src"../../image/1_.jpg"></div><div class"box"><img src"../../image/2_.jpg"></div>…

【Mac】Ghost Buster Pro(苹果电脑内存清理专家) v3.2.5安装教程

软件介绍 Ghost Buster pro是一款针对Mac系统的电脑清理和优化工具&#xff0c;可以帮助用户清理系统垃圾、修复注册表错误、卸载不需要的软件、管理启动项等&#xff0c;从而提高系统性能和稳定性。 安装教程 1.打开镜像包&#xff0c;拖动「Ghost Buster Pro」到应用程序中…

[AutoSar]BSW_Diagnostic_004 ReadDataByIdentifier(0x22)的配置和实现

目录 关键词平台说明背景一、配置DcmDspDataInfos二、配置DcmDspDatas三、创建DcmDspDidInfos四、创建DcmDspDids五、总览六、创建一个ASWC七、mapping DCM port八、打开davinci developer&#xff0c;创建runnabl九、生成代码 关键词 嵌入式、C语言、autosar、OS、BSW、UDS、…

编写Ansible角色实现分布式LNMP安装

前言 本文将介绍如何使用 Ansible 编写角色&#xff0c;在分布式环境下完成 LNMP&#xff08;Linux、Nginx、MySQL、PHP&#xff09;的自动化&#xff08;编译&#xff09;安装和配置&#xff0c;并验证 PHP 与 MySQL 数据联通性&#xff0c;实现博客和论坛页面的展示。 常规…

Vue3路由及登录注销功能、设置导航守护功能模块

路由 在vue中&#xff0c;页面和组件都是.vue文件&#xff0c;可以说是一样的&#xff0c;结构、内容和生产方法都是一样&#xff0c;但是组件可以被反复使用&#xff0c;但页面一般只被使用一次。 路由的作用就是网页地址发生变化时&#xff0c;在App.vue页面的指定位置可以加…

HTTP 连接详解

概述 世界上几乎所有的 HTTP 通信都是由 TCP/IP 承载的&#xff0c;客户端可以打开一条TCP/IP连接&#xff0c;连接到任何地方的服务器。一旦连接建立&#xff0c;客户端和服务器之间交换的报文就永远不会丢失、受损或失序 TCP&#xff08;Transmission Control Protocol&…

2-1 EXTI外部中断(gd32)

中断的概念 中断硬件结构/软件结构 EXTI中断 EXTI硬件结构 注&#xff1a;EXTI线在同一时刻只能连接一个GPIO口&#xff0c;如果我们先连接了PA0,然后又连接了PB0那么此时PA0这个IO口就失去作用。 中断触发函数 中断优先级 中断优先级 数值越小优先级越高&#xff0c;抢占优先级…

加州大学欧文分校英语高级语法专项课程01:Verb Tenses and Passives 学习笔记

Verb Tenses and Passives Course Certificate Course Intro 本文是学习 Verb Tenses and Passives 这门课的学习笔记。 文章目录 Verb Tenses and PassivesWeek 01: Simple, Progressive, and Perfect Verb Tenses ReviewLearning Objectives Present Perfect Tense Review L…

企业级通用业务 Header 处理方案

目录 01: 处理 PC 端基础架构 02: 通用组件&#xff1a;search 搜索框能力分析 03: 通用组件&#xff1a;search 搜索框样式处理 04: 通用组件&#xff1a;Button 按钮能力分析 05: 通用组件&#xff1a;Button 按钮功能实现 06: 通用组件&#xff1a;完善 search 基本…

Photoshop 2022 for Mac/win:释放创意,打造专业级的图像编辑体验

在数字图像编辑的世界里&#xff0c;Adobe Photoshop 2022无疑是那颗璀璨的明星。这款专为Mac和Windows用户设计的图像处理软件&#xff0c;以其卓越的性能和丰富的功能&#xff0c;赢得了全球数百万创作者的青睐。 Photoshop 2022在继承前代版本强大功能的基础上&#xff0c;…

Appium 2.x 安装及使用

由于安全问题&#xff0c;Appium 1.x 版本不再被维护&#xff0c;但想要继续使用Appium进行自动化可以使用 Appium 2.x。 1. 安装Appium 2.x 在过往文章中有介绍过Appium 1.x 的安装&#xff0c;所以一些必备的软件(如&#xff1a;JDK、SDK、node.js、Python)安装就不再细嗦&…

STM32串口通信入门

文章目录 一、串口协议和RS-232标准&#xff0c;以及RS232电平与TTL电平的区别1.串口通信协议2.RS-232标准3.RS232电平与TTL电平的区别4.USB/TTL转232“模块&#xff08;CH340芯片为例&#xff09; 二、补充实验&#xff08;一&#xff09;几个常见的库函数、结构体1.时钟配置函…