SpringBoot多环境配置踩坑指南

 更新时间:2026年03月26日 08:32:20   作者:神奇小汤圆  
这篇文章主要为大家详细介绍了SpringBoot多环境配置的相关方法和避坑指南,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

先说说我的项目情况

我目前维护的项目用的是 Spring Boot 2.5.15,配置文件的组织方式是这样的:

resources/config/dev/hbase.properties
resources/config/dev/zk.properties
resources/config/dev/hbase.xml
resources/config/prod/hbase.properties
resources/config/prod/zk.properties
resources/config/prod/hbase.xml

代码里也是五花八门:有用 @Value 注入的,有用 @ConfigurationProperties 的,还有硬编码用 ClassLoader.getResourceAsStream() 读取的。说实话,这种”历史遗留”代码,懂的都懂。

最近研究配置加载方式的时候,发现门道还挺多。今天就结合自己的理解,把这几个常用参数彻底掰扯清楚。

一、-Xbootclasspath/a:JVM 级别的”后门”

这玩意儿到底干嘛的?

简单来说,这是 JVM 的标准参数,用来在 Bootstrap ClassLoader 的搜索路径末尾追加额外的类路径。Bootstrap ClassLoader 是 JVM 最顶层的类加载器,负责加载核心类库。

我什么时候用它?

主要是为了解决那种需要从 ClassLoader 直接读取配置文件的场景。比如我项目里那些用 getResourceAsStream("xxx.properties") 的代码,如果不加这个参数,有时候就读不到外部配置文件。

用法示例

# Linux/Mac
java -Xbootclasspath/a:/path/to/config -jar app.jar
# Windows(注意用分号)
java -Xbootclasspath/a:C:\path\to\config -jar app.jar

踩坑提醒

  • 路径分隔符:Linux 用 :,Windows 用 ;,搞错了直接报错
  • 会改变 ClassLoader 的加载顺序,有时候会引起一些诡异的类加载问题

二、-Dloader.path:Spring Boot 可执行 Jar 的专属武器

原理是啥?

这个参数是 Spring Boot 的 LaunchedURLClassLoader 专门识别的。当你打包成 Fat Jar 后,Spring Boot 会用自己定制的 ClassLoader 来加载类,而这个参数就是告诉它:”嘿,除了这些,再去这些地方找找”。

我能用它干嘛?

  • 加载外部的配置文件
  • 动态添加额外的 jar 包
  • 实现某种程度上的”热更新”(虽然不建议在生产环境这么玩)

用法示例

# Linux/Mac
java -Dloader.path=/path/to/config,/path/to/lib -jar app.jar
# Windows
java -Dloader.path=C:\path\to\config;C:\path\to\lib -jar app.jar

实际应用场景

我现在的启动脚本就是这样写的:

java -Dloader.path=/opt/myapp/config \
     -Dspring.profiles.active=prod \
     -jar myapp.jar

这样配置文件就可以放在 jar 包外面,改配置不用重新打包,运维同学也很开心。

三、spring.config.location:彻底接管配置加载

注意!这是”替换”不是”追加”

这个参数会完全覆盖 Spring Boot 默认的配置文件搜索位置。默认情况下,Spring Boot 会按这个顺序找配置:

  • file:./config/
  • file:./
  • classpath:/config/
  • classpath:/

一旦你用了 spring.config.location,上面这些位置就都被忽略了,只加载你指定的位置。

三种设置方式

# 1. 命令行参数(优先级最高)
java -jar app.jar --spring.config.location=/path/to/config/
# 2. 系统属性
java -Dspring.config.location=/path/to/config/ -jar app.jar
# 3. 环境变量
export SPRING_CONFIG_LOCATION=/path/to/config/

我的建议

除非你有特殊需求,否则不要轻易用这个参数。一旦用了,默认的配置加载逻辑就全被 干掉了,很容易踩坑。

四、spring.config.additional-location:更友好的”追加”方式

和上面的区别

这个参数是追加而不是替换。它会在默认配置位置的基础上,额外增加你指定的搜索路径。

优先级顺序

  • spring.config.location 指定的位置(最高)
  • spring.config.additional-location 指定的位置
  • 默认位置(最低)

这是我目前最推荐的方式

java -jar app.jar --spring.config.additional-location=/opt/myapp/config/

这样既保留了 Spring Boot 的默认行为,又能加载外部的配置文件,两全其美。

五、配置加载优先级:一定要搞清楚

Spring Boot 的配置属性优先级从高到低:

  • 命令行参数--server.port=8080
  • Java 系统属性System.getProperties()
  • 操作系统环境变量
  • RandomValuePropertySource${random.*}
  • Jar 包外部的 profile 配置文件
  • Jar 包内部的 profile 配置文件
  • Jar 包外部的 application.properties
  • Jar 包内部的 application.properties
  • @PropertySource 注解加载的配置
  • 默认属性(最低)

记住这个优先级很重要,有时候配置没生效,很可能就是优先级被覆盖了。

六、我的项目改造实录

原来的问题

我项目里有一堆这样的代码:

InputStream is = HbaseConfig.class.getClassLoader()
    .getResourceAsStream("hbase.properties");

这种写法有几个问题:

  • 只能从 classpath 根目录加载
  • 无法加载 config/dev/hbase.properties 这种嵌套路径
  • 不支持 Spring Boot 的配置优先级机制

改造方案

方案一:使用 Spring 的 ResourceLoader(推荐)

@Autowired
private ResourceLoader resourceLoader;
public void loadConfig() throws IOException {
    Resource resource = resourceLoader.getResource(
        "classpath:config/" + env + "/hbase.properties"
    );
    Properties props = new Properties();
    props.load(resource.getInputStream());
}

方案二:直接用 @Value 注入

@Value("classpath:config/${spring.profiles.active}/hbase.properties")
private Resource hbaseConfig;

方案三:彻底拥抱 Spring Boot(最佳)

把配置文件内容合并到 application-dev.yml 和 application-prod.yml 中,完全交给 Spring Boot 管理。

七、各环境的最佳实践

开发环境(IDE 中运行)

项目结构:

project/
├── src/main/resources/
│   ├── application.yml          # 主配置
│   ├── application-dev.yml      # 开发环境
│   ├── application-prod.yml     # 生产环境
│   └── config/
│       ├── dev/
│       └── prod/
└── external-config/             # 外部配置(gitignore)
    └── application-local.yml    # 个人本地配置

IDE 启动参数:

-Dspring.profiles.active=dev
-Dspring.config.additional-location=file:./external-config/

Linux 生产环境

目录结构:

/opt/myapp/
├── app.jar
├── config/
│   ├── application.yml
│   ├── hbase.properties
│   └── zk.properties
└── logs/

启动脚本:

#!/bin/bash
APP_HOME=/opt/myapp
CONFIG_DIR=$APP_HOME/config
LOG_DIR=$APP_HOME/logs
mkdir -p $LOG_DIR
java \
  -Dspring.profiles.active=prod \
  -Dspring.config.additional-location=file:$CONFIG_DIR/ \
  -Dloader.path=$CONFIG_DIR \
  -Xms512m \
  -Xmx1024m \
  -jar $APP_HOME/app.jar \
  >> $LOG_DIR/app.log 2>&1 &
echo $! > $APP_HOME/app.pid

Systemd 服务配置:

[Unit]
Description=My Spring Boot Application
After=syslog.target
[Service]
User=appuser
WorkingDirectory=/opt/myapp
Environment="SPRING_PROFILES_ACTIVE=prod"
Environment="SPRING_CONFIG_ADDITIONAL_LOCATION=file:/opt/myapp/config/"
ExecStart=/usr/bin/java -jar /opt/myapp/app.jar
Restart=on-failure
[Install]
WantedBy=multi-user.target

Windows 生产环境

目录结构:

C:\Apps\MyApp\
├── app.jar
├── config\
│   ├── application.yml
│   ├── hbase.properties
│   └── zk.properties
└── start.bat

启动脚本:

@echo off
setlocal

set APP_HOME=C:\Apps\MyApp
set CONFIG_DIR=%APP_HOME%\config

java ^
  -Dspring.profiles.active=prod ^
  -Dspring.config.additional-location=file:%CONFIG_DIR%/ ^
  -Dloader.path=%CONFIG_DIR% ^
  -Xms512m ^
  -Xmx1024m ^
  -jar %APP_HOME%\app.jar

endlocal

八、最后:我的选择建议

场景推荐参数原因
开发环境spring.config.additional-location保留默认行为,方便调试
生产环境(Linux)spring.config.additional-location + loader.path灵活、可控
生产环境(Windows)spring.config.additional-location + loader.path同上
需要 ClassLoader 读取-Xbootclasspath/a兼容遗留代码
完全自定义配置位置spring.config.location特殊需求专用

最后说一句:不要过度设计。对于大部分项目来说,spring.config.additional-location 配合 spring.profiles.active 已经够用了。除非你真的有特殊需求,否则没必要搞得太复杂。

到此这篇关于SpringBoot多环境配置踩坑指南的文章就介绍到这了,更多相关SpringBoot多环境配置内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot+WebSocket实现即时通讯功能(Spring方式)

    SpringBoot+WebSocket实现即时通讯功能(Spring方式)

    今天给大家分享一个SpringBoot+WebSocket实现即时通讯功能(Spring方式),WebSocket是一种在单个TCP连接上进行全双工通信的协议,文章通过代码示例给大家介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • Java项目的目录结构详解

    Java项目的目录结构详解

    本文主要介绍了Java项目的目录结构详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Java实现的KNN算法示例

    Java实现的KNN算法示例

    这篇文章主要介绍了Java实现的KNN算法,结合实例形式分析了KNN算法的原理及Java定义与使用KNN算法流程、训练数据相关操作技巧,需要的朋友可以参考下
    2018-06-06
  • Java二叉树的四种遍历(递归与非递归)

    Java二叉树的四种遍历(递归与非递归)

    这篇文章小编给大家分享的是Java二叉树的四种遍历,主要是递归与非递归,下面文章加u来详细介绍,感兴趣的小伙伴一起来学习吧
    2021-10-10
  • 如何使用Spring Boot实现自定义Spring Boot插件

    如何使用Spring Boot实现自定义Spring Boot插件

    在本文中,我们介绍了如何使用 Spring Boot 实现自定义插件,使用自定义插件可以帮助我们快速地添加一些额外的功能,提高系统的可扩展性和可维护性,感兴趣的朋友跟随小编一起看看吧
    2023-06-06
  • 使用Java实现接口拦截器来监控接口的执行情况

    使用Java实现接口拦截器来监控接口的执行情况

    在排查问题的时候,由于没有对接口的执行情况,以及入参进行监控,所以排查起问题就特别费劲,今天我们就一起来写一个接口的拦截器来监控接口的执行情况吧
    2024-01-01
  • Spring @Value如何通过${}、#{}注入不同类型的值

    Spring @Value如何通过${}、#{}注入不同类型的值

    这篇文章主要介绍了Spring @Value如何通过${}、#{}注入不同类型的值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Java最长公共子序列示例源码

    Java最长公共子序列示例源码

    这篇文章主要介绍了Java最长公共子序列的定义及示例源代码,具有一定参考价值,需要的朋友可以看下。
    2017-09-09
  • Java8 Zip 压缩与解压缩的实现

    Java8 Zip 压缩与解压缩的实现

    这篇文章主要介绍了Java8 Zip 压缩与解压缩的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Java Swing JPanel面板的使用方法

    Java Swing JPanel面板的使用方法

    这篇文章主要介绍了Java Swing JPanel面板的使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12

最新评论