springboot配置注入增强(二)属性注入的原理

news2024/9/25 9:35:41

一 原理

1 配置的存储

springboot在启动的时候会后构建一个org.springframework.core.env.Environment类型的对象,这个对象就是用于存储配置,如图springboot会在启动的最开始创建一个Environment对象

这个webApplicationType的枚举是在new SpringApplication()时候指定的

  • 如果org.springframework.web.reactive.DispatcherHandler存在并且可加载(他本身或其依赖项之一不存在或无法加载),并且org.springframework.web.servlet.DispatcherServlet不存在或不可加载,并且org.glassfish.jersey.servlet.ServletContainer不存在或不可加载,那么就会使用WebApplicationType.REACTIVE,构建ApplicationReactiveWebEnvironment类型的Environment对象,即Spring WebFlux框架
  • 如果javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext中任何一个不存在或不可加载,那么就会使用WebApplicationType.NONE,构建ApplicationEnvironment类型的Environment对象,即普通spring非web框架
  • 否则就会使用WebApplicationType.SERVLET,构建ApplicationServletEnvironment类型的Environment对象,即Servlet也就是spring mvc框架

我们以常用的spring mvc为例,先看下ApplicationServletEnvironment类的数据结构(都大同小异)

本质是一个PropertyResolver接口,核心是能提供一个根据配置中某个属性的key获取对应属性值的方法和跟据某个规则解析属性值的方法,他所有的子类都是在对其做一些扩张,让其使用更方便,比如ConfigurableEnvironment增加的MutablePropertySources getPropertySources()方法,就是为了能获取到全部的配置内容

其实springboot的这三种Environment都是StandardEnvironment的子类,而StandardEnvironment的父类AbstractEnvironment使用了MutablePropertySources作为数据源集合的类型,当然它也是根据其实现接口ConfigurableEnvironment中的MutablePropertySources getPropertySources()方法来确定数据源集合的类型。

可以看出来这个类对比原始的PropertyResolver接口多一个数据源集合,springboot的配置原理简单来说就是将不同来源的配置组装成不同数据源类型的数据源对象,然后放到MutablePropertySources中根据名称和数据源对象进行key-value存储,使用的时候遍历MutablePropertySources中所有的数据源的value,从中找到第一个符合条件的值,找到之后再进行解析,比如${xxxx}这种。这个使用的逻辑即PropertyResolver接口的全部实现是由PropertySourcesPropertyResolver对象代理的(在AbstractEnvironment的构造方法中会new一个PropertySourcesPropertyResolver对象)

MutablePropertySources中有一个List<PropertySource<?>> propertySourceList成员变量,这个就是上面说的数据源集合

PropertySource就是具体的配置了,其实也就两个变量,name:数据源名称,source:具体的数据源。这个实现类有很多,我们也可以自己定义,比如自己创建一个类用做source,然后实现PropertySource那几个根据source查询值的方法

2 配置的来源

其实这个配置的来源可以任何时候添加到Environment对象中,只不过如果想让springboot在启动过程中加载bean时使用到我们的数据源,我们应该在PropertySourcesPlaceholderConfigurer的postProcessBeanFactory()方法执行前加进Environment中,低版本的springboot用的是PropertyPlaceholderConfigurer,不过这个早就已经弃用了所以也就不用管他了,下面我们介绍下PropertySourcesPlaceholderConfigurer

可以看到他是一个BeanFactoryPostProcessor实现类,这个类会在启动的refresh阶段执行postProcessBeanFactory()方法

可以看到,这里会新建一个数据源集合,并且把environment和localProperties加进去,这个localProperties就是我们手动构建PropertySourcesPlaceholderConfigurer时指定的配置文件路径

  @Bean
  public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    configurer.setLocation(new ClassPathResource("config.properties"));
    return configurer;
  }

之后会将这个数据源集合封装为一个StringValueResolver用于解析属性

可以看到这个先被用于解析bean的属性如这种的${xx},其实我感觉这个也是为了兼容老代码,毕竟最早的spring也只有在配置文件里注入的这种方式

<bean id="cacheService" class="my.user.UserImpl">
        <property name="name" value="${user.123.name}"/>
    </bean>

然后可以看到执行了beanFactoryToProcess.addEmbeddedValueResolver(valueResolver)方法,这个方法只是将这个valueResolver解析器存了起来,现在还没用。

会等到解析@value时候在AutowiredAnnotationBeanPostProcessor用到,下面介绍几个系统配置的常用的数据源        

2.1 系统的环境变量和java启动时的启动参数

StandardEnvironment在初始化时会由父类执行StandardEnvironment的customizePropertySources方法创建两个数据源放到数据源集合,这两个数据源我们也很熟悉,就是系统的环境变量和java启动时的启动参数即System.getenv()和System.getProperties()

2.2 application.yml

旧版本是由ConfigFileApplicationListener作为启动监听器,在监听到ApplicationEnvironmentPreparedEvent事件也就是创建完Environment后会发的一个事件中加载的

可以看到他也实现了EnvironmentPostProcessor接口,并把自身和其他spring.factories文件中配置的org.springframework.boot.env.EnvironmentPostProcessor=xxxx,合并一起执行对应的postProcessEnvironment()方法,而他本身的postProcessEnvironment方法会加载application.yml文件

新版本是用ConfigDataEnvironmentPostProcessor加载的,同时ConfigFileApplicationListener被废弃了,改为由EnvironmentPostProcessorApplicationListener来执行EnvironmentPostProcessor的方法,职责单一更清晰了些

2.3 PropertySources/PropertySource

PropertySources其实就是PropertySource的集合,是由ConfigurationClassPostProcessor将PropertySource路径的配置文件内容添加到environment中

可以看到这个路径甚至还支持${xxx}这种动态路径(environment.resolveRequiredPlaceholders会将${xxx}从environment对象中获取真正的值),然后将多个location解析后的propertySource组合起来形成一个CompositePropertySource对象

3 配置的使用

3.1 BeanDefinition 的属性注入

上面介绍过,通常就是对BeanDefinition的propertyValues进行解析,在PropertySourcesPlaceholderConfigurer里

<bean id="cacheService" class="my.user.UserImpl">
        <property name="name" value="${user.123.name}"/>
    </bean>

3.2 @Value

上面也介绍过在AutowiredAnnotationBeanPostProcessor会进行解析

    @Value("${user.123.name}")
    private String user123Name;

3.3 @ConfigurationProperties

@Data
@Component
@ConfigurationProperties(prefix = "user.123")
public class UserConfiguration {
    /**
     * 姓名
     */
    private String name;

    /**
     * 性别
     */
    private String sex;
}

他是由ConfigurationPropertiesBindingPostProcessor的postProcessBeforeInitialization(Object bean, String beanName)来进行绑定的

可以看到实际上执行的是Binder的bind()方法,这个方法可以将以某个相同前缀的属性绑定到对应对象的属性上,主要关注Binder构造器的前两个参数Iterable<ConfigurationPropertySource> sources, PlaceholdersResolver placeholdersResolver,可以看到都是用propertySources作为数据源来进行查找和解析的,而这个propertySources是ConfigurationPropertiesBinder.register(registry)方法中调用ConfigurationPropertiesBinder.Factory#create()方法中生成的

可以看到如果只有一个PropertySourcesPlaceholderConfigurer类型的bean时,这个数据源就是我们上面说的那个和3.1和3.2所用的一样的数据源PropertySourcesPlaceholderConfigurer的appliedPropertySources。否则就会用Environment作为数据源

3.4 environment

这个就简单了,直接从environment对象取即可

  • environment.getProperty("user.123.name")
  • environment.resolvePlaceholders("${user.123.name}")
  • environment.resolveRequiredPlaceholders("${user.123.name}") 如果解析不到会报错

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

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

相关文章

Learn Prompt-Midjourney 图片生成

简介 随着 ChatGPT 的爆火&#xff0c;越来越多的人开始关注并尝试 AI 相关的应用。而图片生成就是其中一个备受瞩目的领域。目前已经有许多图像生成工具&#xff0c;如 Midjourney&#xff0c;Stable Diffusion&#xff0c;DALL-E&#xff0c;Firefly等。本课程主要是以 Midj…

Python 变量的类型注解

视频版教程 Python3零基础7天入门实战视频教程 类型注解 前面有个示例&#xff0c;我们函数参数是字符串类型&#xff0c;但是pycharm工具不知道&#xff0c;所以不会给我们提示s的方法&#xff0c;我们只能人工手写&#xff0c;效率大大降低。 类型注解&#xff1a; 在代码中…

心法利器[101] | 从大模型到大模型系统

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

使用ElementPlus实现内嵌表格和内嵌分页

前言 有时遇到这样的需求&#xff0c;就是在表格里面嵌入一个表格&#xff0c;以及要求带有分页&#xff0c;这样在ElementPlus中很好实现。以下使用Vue2语法实现一个简单例子&#xff0c;毕竟Vue3兼容Vue2语法&#xff0c;若想要Vue3版本例子&#xff0c;简单改改就OK了。 一…

数据结构——图的应用

文章目录 前言一、图的应用1. 最小生成树普里姆&#xff08;Prim&#xff09;算法克鲁斯卡尔&#xff08;Kruskal&#xff09;算法 2. 最短路径Dijkstra算法求单源最短路径 3. 拓扑结构4. 关键路径 总结 前言 图的应用 1.1 最小生成树 1.2 最短路径 1.3 拓扑结构 1.4 关键路径…

【网络教程】揭秘Windows SSH服务端免密登录:告别繁琐,享受安全连接

文章目录 开启Windows下的SSH服务端图形界面安装手动下载安装Windows如何查看系统用户名Windows如何查看本机IP开启免密登录Window生成秘钥Linux下生成秘钥配置公钥视频讲解开启Windows下的SSH服务端 这篇文章演示的环境是Windows11Windows的SSH服务端默认情况下是没有安装的,…

9.项目细节调整

文章目录 学习资料项目整体细节调整处理首页 学习资料 https://www.bilibili.com/video/BV13g411Y7GS/?p24&spm_id_frompageDriver&vd_sourceed09a620bf87401694f763818a31c91e 项目整体细节调整 处理首页 默认的首页 肯定不能给人看。文件在这个位置。 可以删除…

live555 BasicUsageEnvironment目录解读

文章目录 BasicUsageEnvironmentBasicHashTable.cppBasicTaskScheduler.cppBasicTaskScheduler0.cppBasicUsageEnvironment.cppBasicUsageEnvironment0.cppDelayQueue.cpp后续会进行更新 BasicUsageEnvironment ├── BasicHashTable.cpp ├── BasicTaskScheduler0.cpp ├─…

类加载器 - 双亲委派模型

文章目录 回顾一下类加载过程类加载器类加载器介绍类加载器加载规则类加载器总结自定义类加载器 双亲委派模型双亲委派模型介绍双亲委派模型的执行流程双亲委派模型的好处打破双亲委派模型方法 本文参考&#xff1a; 类加载器详解&#xff08;重点&#xff09; | JavaGuide(Ja…

多目标优化算法:基于非支配排序的海象优化算法(NSWOA)MATLAB

一、海象优化算法WOA 海象优化算法&#xff08;Walrus Optimization Algorithm&#xff0c;WOA&#xff09;由Trojovsk等人于2023年提出&#xff0c;该算法模拟海象的进食&#xff0c;迁移&#xff0c;逃跑和对抗捕食者的过程&#xff0c;WOA包含探索、迁移和开发三个阶段&…

正则表达式学习和高级用法

以下所有的验证都在 在线验证 1. 起始符 / 正则表达式的起始符2. 限定符 匹配前面的子表达式**1次或多次**。例如&#xff0c;zo 能匹配 "zo" 以及"zoo"&#xff0c;但不能匹配 "z"。等价于 {1,}。 ? 匹配前面的子表达式**0次或1次**。例如…

二进制 Deploy Kubernetes v1.23.17 超级详细部署

文章目录 1. 预备条件2. 基础配置2.1 配置root远程登录2.2 配置主机名2.3 安装 ansible2.4 配置互信2.5 配置hosts文件2.6 关闭防firewalld火墙2.7 关闭 selinux2.8 关闭交换分区swap2.9 修改内核参数2.10 安装iptables2.11 开启ipvs2.12 配置limits参数2.13 配置 yum2.14 配置…

函数防抖和节流

函数防抖和节流 经典真题 防抖&#xff0c;节流是什么&#xff0c;如何实现 &#xff08;字节&#xff09; 什么是函数防抖和节流 JavaScript 中的函数大多数情况下都是由用户主动调用触发的&#xff0c;除非是函数本身的实现不合理&#xff0c;否则一般不会遇到跟性能相关…

DMA简单总结

文章目录 一、基础概念1.1 DMA---Direct Memory Access 直接存储器访问&#xff0c;目的减少CPU资源占用 二、典型DMA硬件模型2.1 基本硬件特性---通道数、源/目标类型&#xff0c;地址与累加方式&#xff0c;数据位宽&#xff0c;搬移长度&#xff0c;循环模式&#xff0c;中断…

在c#中使用CancellationToken取消任务

目录 &#x1f680;介绍&#xff1a; &#x1f424;简单举例 &#x1f680;IsCancellationRequested &#x1f680;ThrowIfCancellationRequested &#x1f424;在控制器中使用 &#x1f680;通过异步方法的参数使用cancellationToken &#x1f680;api结合ThrowIfCancel…

‘XXX’ is already defined @typescript-eslint/no-redeclare 警告 问题解决

上文ReactTypescript项目环境中搭建并使用redux环境 结束是 其实不算完全写完吧 还会留下一个警告 这个报错 好像是说 这两个值已经定义过了 可能很多人 会觉得小问题 但你会发现 无论你名字怎么改都会一直 带着你新的名字继续报错 我们可以多套一层 export namespace Const…

Java8实战-总结29

Java8实战-总结29 并行数据处理与性能并行流将顺序流转换为并行流测量流性能 并行数据处理与性能 到目前为止&#xff0c;Stream接口最重要的好处是可以对这些集合执行操作流水线&#xff0c;能够自动利用计算机上的多个内核。 例如&#xff0c;在Java 7之前&#xff0c;并行…

Python 异常的概念

视频版教程 Python3零基础7天入门实战视频教程 异常是指程序运行的过程中出现了错误&#xff0c;也叫Bug。 案例一&#xff1a; # 定义人类 class Person:# 属性 姓名name None# 属性 年龄age Noneprint(Person().sex)因为Person类没有sex属性&#xff0c;对象访问sex属性…

Go语言开发环境搭建指南:快速上手构建高效的Go开发环境

Go 官网&#xff1a;https://go.dev/dl/ Go 语言中文网&#xff1a;https://studygolang.com/dl 下载 Go 的语言包 进入官方网站 Go 官网 或 Go 语言中文网&#xff1a; 选择下载对应操作系统的安装包&#xff1a; 等待下载完成&#xff1a; 安装 Go 的语言包 双击运行上…

HuggingFace Transformer

NLP简介 HuggingFace简介 hugging face在NLP领域最出名&#xff0c;其提供的模型大多都是基于Transformer的。为了易用性&#xff0c;Hugging Face还为用户提供了以下几个项目&#xff1a; Transformers(github, 官方文档): Transformers提供了上千个预训练好的模型可以用于不…