java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot配置修改后未生效

SpringBoot中配置修改后未生效的常见原因与解决方案

作者:元界MetaLite

本文深入剖析Spring Boot中配置修改后未生效的常见原因,重点区分JVM系统属性、Spring Environment及static final字段的读取时机与作用域,并提供按PropertySource来源排查、避免静态缓存误用、安全输出配置来源等实用方法,需要的朋友可以参考下

开篇

配置文件里已经改成 prod,工具类却还说不是生产环境。先别急着重启:它可能根本没从 Spring 配置里读值。下面用一个环境工具类区分 JVM 系统属性、Spring 配置,以及类初始化时保存的值。

环境:JDK 21、Spring Boot 3、Spring Framework 6。EnvUtil 是应用自有工具;示例在独立 JVM 中修改测试属性,不连接 Nacos,也不修改运行中的业务服务。

坑点

坑点一:属性名一样,就以为读取位置也一样

工具类读取的是 System.getProperty,不是 Spring Environment。配置文件、环境变量和 JVM 的 -D 参数不能因此混成一回事:

public class EnvUtil {
    /**
     * VM OPTS中获取运行环境:dev/test/prod
     */
    public static final String ENVIRONMENT = System.getProperty("spring.profiles.active");
    /**
     * VM OPTS中获取应用名称
     */
    public static final String APPLICATION_NAME = System.getProperty("spring.application.name");

    private EnvUtil() {

    }

    /**
     * 判断当前环境是否为开发环境
     */
    public static boolean isDev() {
        return "dev".equals(ENVIRONMENT);
    }

    /**
     * 判断当前环境是否为测试环境
     */
    public static boolean isTest() {
        return "test".equals(ENVIRONMENT);
    }

    /**
     * 判断当前环境是否为生产环境
     */
    public static boolean isProd() {
        return "prod".equals(ENVIRONMENT);
    }

}

原注释已经明确是 VM OPTS。把 spring.profiles.active 写进 YAML,并不会自动等同于设置同名 JVM 系统属性。

坑点二:static final 保存了一次值,就以为以后还能跟着变

ENVIRONMENT 在类初始化时赋值。后续修改系统属性,这个字段不会自动重新读取,isProd() 也只是比较已经保存的字符串。

正确写法

避坑一:按读取入口提供配置,排查时同时看来源

若采用上面的 EnvUtil,就在应用加载它之前,使用 JVM 参数提供固定值:

java -Dspring.profiles.active=prod -Dspring.application.name=demo -jar app.jar

-D 放在 -jar 前,由 JVM 处理。它与 Spring Boot 的 --spring.profiles.active=prod 不是同一个输入入口;后者通常进入 Spring 的属性解析,不应假定同步写回 System.getProperty。

下面用本地属性源演示为什么同名配置可以读到不同值:

System.setProperty("spring.application.name", "vm-name");

StandardEnvironment environment = new StandardEnvironment();
environment.getPropertySources().addFirst(new MapPropertySource(
        "demo-config",
        Map.of("spring.application.name", "spring-name")));

System.out.println(System.getProperty("spring.application.name"));
// vm-name
System.out.println(environment.getProperty("spring.application.name"));
// spring-name

addFirst 是这个演示明确指定的顺序,不是对所有配置文件、命令行和配置中心优先级的总排序。排查真实应用时,要看目标应用实际装载的 PropertySource。

避坑二:固定启动属性按快照使用,动态值通过动态入口读取

以下调用放在首次使用 EnvUtil 的独立 JVM 中。先设置 test,再初始化工具类,随后将系统属性改为 prod:

System.setProperty("spring.profiles.active", "test");
System.out.println(EnvUtil.ENVIRONMENT);
// test

System.setProperty("spring.profiles.active", "prod");
System.out.println(System.getProperty("spring.profiles.active"));
// prod
System.out.println(EnvUtil.ENVIRONMENT);
// test
System.out.println(EnvUtil.isProd());
// false

工具类适合明确固定的启动参数,不适合拿来判断配置是否热更新。若需求本来就是动态读取,应使用相应的 Spring 配置读取或绑定机制,并验证刷新行为,不能只把启动参数换个地方写。

排查时输出什么

先确认调用的是 System.getProperty、Environment.getProperty,还是某个已赋值的字段。必要时只对非敏感测试项打印来源名称:

String key = "spring.application.name";
for (PropertySource<?> source : environment.getPropertySources()) {
    if (source.containsProperty(key)) {
        System.out.println(source.getName());
        // 本例依次打印 demo-config、systemProperties。
    }
}

不要全量打印配置,密码、令牌和连接信息可能混在其中。Nacos 是否装载、刷新,以及最终优先级,要在对应应用环境另行检查;进入 Spring Environment 不等于写回 JVM 系统属性。

还有一个格式边界:isProd() 只接受精确字符串 prod。prod,metrics 这样的多 profile 字符串不会被视为 prod;需要判断激活集合时,应使用 Spring 的 profile 语义。

总结

配置改了却不生效,先看代码从哪里读、什么时候读。这里的工具类保存的是启动时的 JVM 属性快照;修改 Spring 配置或稍后改系统属性,都不能让这个字段自动更新。

以上就是SpringBoot中配置修改后未生效的常见原因与解决方案的详细内容,更多关于SpringBoot配置修改后未生效的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文