You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot运行时如何通过Controller修改yaml文件占位符值

Spring Boot运行时动态修改application.yml配置实现

首先修正你提供的application.yml的语法错误:标准yaml要求键值对为key: value结构,你当前写的配置缺少值对应的键名,会导致解析失败,修正后的示例如下:

resource: 
  value: KL-MED123-DEV    # 需根据用户响应修改为不同值
client: 
  value: CLI-AR234        # 需根据用户响应修改为不同值

以下实现同时满足配置持久化到文件、运行时立即生效、支持其他配置项引用新值的要求。

1. 基础配置

先配置外部yaml加载路径,避免jar部署时无法写入内部文件的问题:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;

@Configuration
public class YamlConfig {
    // 固定读取jar包同级config目录下的application.yml
    public static final String YAML_PATH = System.getProperty("user.dir") + "/config/application.yml";

    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        configurer.setLocation(new FileSystemResource(YAML_PATH));
        return configurer;
    }
}

2. Yaml读写工具类

封装yaml的读取、写入、嵌套键修改逻辑,Spring Boot已内置SnakeYaml依赖,无需额外引入:

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class YamlUtil {
    public static Map<String, Object> loadYaml(String path) throws IOException {
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        Yaml yaml = new Yaml(options);
        try (InputStream in = new FileInputStream(path)) {
            return yaml.load(in);
        }
    }

    public static void writeYaml(String path, Map<String, Object> content) throws IOException {
        DumperOptions options = new DumperOptions();
        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        options.setPrettyFlow(true);
        Yaml yaml = new Yaml(options);
        try (Writer writer = new FileWriter(path)) {
            yaml.dump(content, writer);
        }
    }

    @SuppressWarnings("unchecked")
    public static void setValue(Map<String, Object> map, String keyPath, Object value) {
        String[] keys = keyPath.split("\\.");
        Map<String, Object> current = map;
        for (int i = 0; i < keys.length - 1; i++) {
            current = (Map<String, Object>) current.getOrDefault(keys[i], new HashMap<>());
        }
        current.put(keys[keys.length - 1], value);
    }
}

3. Controller层实现逻辑

修改文件后同步刷新Spring内存中的配置源,保证配置立即生效:

import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@RestController
public class PingController {

    private final ConfigurableEnvironment environment;

    public PingController(ConfigurableEnvironment environment) {
        this.environment = environment;
    }

    @GetMapping("/ping")
    public ResponseEntity<String> ping(@RequestParam String resourceVal, @RequestParam String clientVal) throws IOException {
        String yamlPath = YamlConfig.YAML_PATH;
        // 读取现有配置
        Map<String, Object> yamlContent = YamlUtil.loadYaml(yamlPath);
        // 更新目标配置值,key路径根据实际yml结构调整
        YamlUtil.setValue(yamlContent, "resource.value", resourceVal);
        YamlUtil.setValue(yamlContent, "client.value", clientVal);
        // 写入文件持久化,保证应用重启后配置不丢失
        YamlUtil.writeYaml(yamlPath, yamlContent);

        // 刷新内存配置源,让新值立即生效
        MutablePropertySources propertySources = environment.getPropertySources();
        Properties props = new Properties();
        props.putAll(flattenMap(yamlContent, ""));
        String sourceName = "applicationConfig: [file:" + yamlPath + "]";
        propertySources.replace(sourceName, new PropertiesPropertySource(sourceName, props));

        return ResponseEntity.ok(String.format("配置更新成功,当前resource=%s,client=%s",
                environment.getProperty("resource.value"), environment.getProperty("client.value")));
    }

    // 嵌套map打平为Spring配置兼容的一维key-value结构
    @SuppressWarnings("unchecked")
    private Map<String, Object> flattenMap(Map<String, Object> source, String parentKey) {
        Map<String, Object> result = new HashMap<>();
        for (Map.Entry<String, Object> entry : source.entrySet()) {
            String key = parentKey.isEmpty() ? entry.getKey() : parentKey + "." + entry.getKey();
            Object value = entry.getValue();
            if (value instanceof Map) {
                result.putAll(flattenMap((Map<String, Object>) value, key));
            } else {
                result.put(key, value);
            }
        }
        return result;
    }
}

接口调用示例:/ping?resourceVal=KL-MED456-PROD&clientVal=CLI-XY987,传入的参数即为要更新的目标配置值。

注意事项

  • 若采用jar包部署,禁止修改jar包内部打包的application.yml文件,必须将yml放在jar包同级的config目录下加载外部配置,jar包运行时内部文件为只读状态,直接写入会抛出IO异常。
  • 若项目集成了Spring Cloud,只需给所有依赖配置值的Bean添加@RefreshScope注解,配置修改后调用上下文的refresh()方法即可自动刷新所有注入的配置值,无需手动替换PropertySource。
  • 配置写入逻辑需要加分布式锁或本地同步锁,避免多请求并发写入导致yaml文件格式损坏。
  • 只要内存中的配置源完成刷新,yaml中其他引用这两个值的占位符会自动读取最新值,无需额外处理引用关系。

内容的提问来源于stack exchange,提问作者Rahul Maurya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 17:06:57