当前位置: 首页 > article >正文

Spring 配置绑定原理分析

Spring 配置绑定原理分析

前言

Spring 应用中存在诸多配置,有的是系统配置,有的命令行启动参数配置,有的是yaml配置,有的是分布式配置中心配置,但对使用者而言总是可以通过@ConfigurationProperties将它关联到一个JavaBean当中或者使用@Value绑定,使得获取这这些来自不同地方的配置就像获取一个对象属性那么简单,那么它是如何来完成这个工作的呢?

结构层次

AbstractEnvironment是Spring环境的抽象实体,它存在一个MutablePropertySources类型(意为可变的配置源)的变量 propertySources,后者维护了系统中所有的配置源,系统环境变量可以是一个配置源,启动参数可以是一个配置源,application.yaml可以是一个配置源,甚至我们可以自定义一个配置源

image-20241106210028035

数据源之间的冲突

我们定义了这么一个配置,我们指定了hk.name的默认值

@Data
@Configuration
@ConfigurationProperties("hk")
public class HkConfig {
    private String name  = "default";
}

我们在application.yaml做出如下配置

hk:
  name: huakai

然后在启动参数也做出配置

--hk.name=hualuo

那么最终我们获取到的hk.name应该是哪一个呢?

答案是这是一个约定,原则是通常遵循着"靠近应用优先原则",通常情况下,

优先级顺序一般如下(从高到低):

  1. 命令行参数
  2. application.propertiesapplication.yml
  3. 操作系统环境变量
  4. JNDI 属性
  5. JVM 系统属性
  6. @PropertySource 注解配置的属性文件
  7. 默认值(在代码中指定的默认值)

实现上是如何做的呢

我们可以看到MutablePropertySources自身维护着一个List<PropertySource<?>>而配置的优先级别决定于配置源在list中的位置,配置源所处的位置越靠前那么它的优先级越高,后者又是因为Spring的策略是遍历配置源如果找到立即返回,这在Binder的实现中可见一斑

private ConfigurationProperty findProperty(ConfigurationPropertyName name, Context context) {
    if (name.isEmpty()) {
       return null;
    }
    for (ConfigurationPropertySource source : context.getSources()) {
       ConfigurationProperty property = source.getConfigurationProperty(name);
       if (property != null) {
          return property;
       }
    }
    return null;
}

MutablePropertySources

这个可变的数据源,提供了一些添加数据源的方法,包括以下两个

addFirst()

​ 这意味着被添加的数据源将最高的优先级

addLast()

​ 这意味着被添加的数据源将最低的优先级

	public void addFirst(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(0, propertySource);
		}
	}

	public void addLast(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(propertySource);
		}
	}

自定义PropertySources

我们定义一个指定的数据源并且期望它的优先级最高

CustomPropertySource

package com.huakai.springenv.config;

import org.springframework.core.env.PropertySource;
import java.util.HashMap;
import java.util.Map;

public class CustomPropertySource extends PropertySource<Object> {

    private final Map<String, String> properties = new HashMap<>();

    public CustomPropertySource(String name) {
        super(name);
        // 在此添加您自定义的属性
        properties.put("hk.name", "customValue");
    }

    @Override
    public Object getProperty(String name) {
        return properties.get(name);
    }
}

CustomPropertySourcePostProcessor

package com.huakai.springenv.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;

public class CustomPropertySourcePostProcessor implements EnvironmentPostProcessor {



    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        CustomPropertySource customPropertySource = new CustomPropertySource("customPropertySource");
        propertySources.addFirst(customPropertySource);
    }
}

spring.factories

org.springframework.boot.env.EnvironmentPostProcessor=com.huakai.springenv.config.CustomPropertySourcePostProcessor

ps:EnvironmentPostProcessor 在 Spring 应用上下文初始化之前就被加载和执行所以只能通过该方式配置

测试

@Resource
private HkConfig hkConfig;

@RequestMapping("testGetConfig")
public String test2() {
    return hkConfig.getName();
}

image-20241106214520252


http://www.kler.cn/a/388196.html

相关文章:

  • Vue7种组件之间通信方式
  • 2-UML概念模型测试
  • yolov7论文翻译
  • 从0开始学PHP面向对象内容之(常用魔术方法续一)
  • C 语言标准库 - <errno.h>
  • 苍穹外卖 数据可视化
  • 安全编码实践:反射API的“间谍游戏”
  • java-web-web后端知识小结
  • 让金融数据处理更精准-C#银行回单识别集成示例、回执单识别
  • GNU/Linux - /proc/sys/vm/overcommit_memory
  • 《Python 与 SQLite:强大的数据库组合》
  • thinkphp如何查出值是null的布尔类型的值
  • 代码随想录算法训练营Day13 | 二叉树理论基础、递归遍历、迭代遍历、统一迭代、层序遍历
  • Android智能座驾,carlink场景截屏黑屏问题
  • Pycharm远程调试deepspeed!可用!
  • 前端三件套配合豆包MarsCode 实现钉钉官网动画
  • USB学习(上)
  • 「Mac玩转仓颉内测版1」入门篇1 - Cangjie环境的搭建
  • NLP之ASR之moonshine:moonshine的简介、安装和使用方法、案例应用之详细攻略
  • 如何设置定时关闭或启动整个docker而不是某个容器
  • GPIO 唤醒深度睡眠的esp32-c3
  • 如何找到养生生活视频素材?推荐几个优秀网站
  • 每日一题之成绩排序(进阶版)
  • springboot静态资源映射不生效问题
  • Node.js——fs模块-相对路径的bug与解决
  • 机器学习—多类