惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - 青石路

好好的OceanBase官方驱动你不用,非要用第三方驱动,ArrayIndexOutOfBoundsException了吧 记一次Qoder排查mave-shade-plugin只重定向部分package导致的NoSuchMethodError问题,查的还挺快的 如果写Redis序列化与读Redis序列化不一致,你觉得会发生什么 记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来 Claude Code安装,接入阿里云百炼模型,蹭蹭免费额度 异源数据同步 → 记一次 DataX 已同步数据量优化 明明连接的是Redis的DB0,为什么能查到DB3的数据? TINYINT(1) 类型的字段,明明数据存的是 2,为什么查出来是 true 用了MySQL的INSERT ON DUPLICATE KEY UPDATE,怎么还报唯一索引冲突错误 那些年不该放到事务中的操作,你实现过哪些 关于布尔类型的变量不要加 is 前缀,被网友们吐槽了,特来完善下 都说了布尔类型的变量不要加 is 前缀,非要加,这不是坑我了嘛 安全漏洞修复导致SpringBoot2.7与Springfox不兼容,问题排查与处理 记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑 经由同个文件多次压缩的文件MD5都不一样问题排查,感慨AI的强大! 记一次cannot access its superinterface问题的的排查 → 强如Spring也一样写Bug SpringBoot支持Kafka多源配置的同时还要支持启停配置化,是真的会玩 如果XXL-JOB执行器在执行某任务中被重启了,重启后该任务能够被自动弥补调度吗 不依赖 Spring,你会如何自实现 RabbitMQ 消息的消费(一) 异源数据同步 → DataX 同步启动后如何手动终止? 异源数据同步 → 如何获取 DataX 已同步数据量? 记一次 RabbitMQ 消费者莫名消失问题的排查 不升级 POI 版本,如何生成符合新版标准的Excel 2007文件 以MySQL为例,来看看maven-shade-plugin如何解决多版本驱动共存的问题? maven 插件之 maven-shade-plugin,解决同包同名 class 共存问题的神器
Spring Boot读取外部配置文件失败,原因绝对出乎你意料
青石路 · 2024-12-03 · via 博客园 - 青石路

开心一刻

今天和相亲对象见面,特意打扮了一番
见完面回到家后我给她发微信
我:我今天的形象怎么样
她:挺白净亮眼的
我:头发不油吧
她:反光,没看清
我:???

反光没看清

知识回顾

在我们的实际开发工程中,打包的 jar 通常会包含配置文件(例如:application.yml)来作为默认配置文件,然后在不同的环境用外部配置文件来覆盖 jar 包中配置文件配置的某些配置项,当然也可以全量覆盖;Spring Boot 关于外部配置(Externalized Configuration)有这么一段说明

External Application Properties

我给大家翻译一下

应用启动时,Spring Boot会自动从以下位置查找并加载 application.propertiesapplication.yaml

  1. 从类路径

    a. 类路径根目录

    classpath根目录

    src/main/resources 下的文件,默认情况下会被打包到类路径(classpath)下

    b. 类路径下的 config

  2. 从当前目录

    a. 当前目录,也就 jar 所在目录

    b. 当前目录下的 config 目录

    c. 当前目录下的 config 目录的直接子目录

Spring Boot 会按如上顺序从上往下查找并加载 application.propertiesapplication.yaml,如果配置项重命名了,后加载的值会覆盖掉之前加载的值。配置文件中的配置项会以 PropertySources 实例的形式添加到 Spring 环境中

我们来看个示例:spring-boot-external-config,代码非常简单,我们只需要关注 ConfigDemo.java

/**
 * @author: 青石路
 */
@Component
public class ConfigDemo implements InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigDemo.class);

    @Value("${retry.times}")
    private Integer retryTimes;
    @Value("${http.url}")
    private String httpUrl;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("retryTimes:{}, httpUrl:{}", retryTimes, httpUrl);
    }
}

application.yml 内容如下

retry:
  times: 6
http:
  url: http://localhost:8080

我相信你们都能看懂,通过 Spring 注入进来两个配置项(retry.timeshttp.url)值,当应用中的全部属性都设置完成之后,Spring 会调用 afterPropertiesSet 方法,日志输出 retryTimeshttpUrl 的值。结合上述的加载顺序,我们来验证下是否如 Spring Boot 官方所说

  1. 从类路径

    这个我们只验证一种情况:类路径下只存在 application.yml;验证非常简单,直接在 jar 所在目录下

    类路径下配置文件生效

    执行

    java -jar spring-boot-external-config-1.0-SNAPSHOT.jar
    

    可以看到输出如下

    类路径下配置文件生效验证

    是不是没毛病?

  2. 从当前目录

    在 jar 所在目录下放一个 application.yml

    当前目录下配置文件

    其内容如下

    retry:
      times: 2
    

    我特意拿掉了配置项 http.url,同样直接执行

    java -jar spring-boot-external-config-1.0-SNAPSHOT.jar
    

    输出结果如下

    当前目录下配置文件生效

    红框框住的值与蓝框框住的值,它们分别来自哪个配置文件,你们应该知道吧;也如 Spring Boot 官方所说,非常正常。我们再在当前目录下加个 config 目录

    当前目录下config配置文件

    其下放一个 application.yml,内容如下

    retry:
      times: 9
    http:
      url: http://127.0.0.1:8080
    

    执行结果如下

    当前目录下config配置文件验证

    也如 Spring Boot 官方描述的那样,没有任何毛病

    为了保持部署结构的简单清晰,我们往往会采用 config 目录的这种方式来放外部配置文件,包括应用配置文件、日志配置文件等等;但如果外部配置文件跟当前目录没有直接关系了,比如在其他盘或者其他目录下,那么如何指定外部配置文件呢?有但不限于如下两种(仔细斟酌如下写法是否正确

    1. java -jar spring-boot-external-config-1.0-SNAPSHOT.jar -Dspring.config.location=外部文件路径
    2. java -jar spring-boot-external-config-1.0-SNAPSHOT.jar --spring.config.location=外部文件路径

    外部文件按路径可以是绝对路径,也可以是相对路径,如果是相对路径,则以 jar 包所在的目录开始算

复现问题

一切准备就绪,我们往 Linux 服务器上部署,先将 spring-boot-external-config-1.0-SNAPSHOT.jar 上传到服务器,执行

java -jar spring-boot-external-config-1.0-SNAPSHOT.jar

日志输出如下

linux上类路径配置

这是 jar 中默认配置文件的配置值,没问题吧?我们再上传 jar 同级目录下的 config 文件夹,上传后目录结构如下

Linux上目录结构

执行后日志输出如下

linux上config配置

也和在 windows 上的演示结果一样,很正常。绝大部分情况下,我们的配置文件不止一个,所以从命名的准确性考虑,我们往往会将 config 目录命名成 configs

linux上configs配置

那么此时启动命令就需要调整下了

java -jar spring-boot-external-config-1.0-SNAPSHOT.jar -Dspring.config.location=configs/application.yml

java -jar spring-boot-external-config-1.0-SNAPSHOT.jar --spring.config.location=configs/application.yml

我们以 -D 的形式启动下,日志输出如下

linux上configs配置-D

这输出的还是 jar 包中的配置项值,并非 configs 目录下 application.yml 中的配置项值,读取外部配置文件失败了?带着疑问我们尝试下 -- 方式

java -jar spring-boot-external-config-1.0-SNAPSHOT.jar --spring.config.location=configs/application.yml

发现能够正常加载

linux上configs配置--

为什么 -D 的方式会失败?

why

解决问题

出题出在哪,就出在 java 命令参数的顺序上,我们看下 java 命令的帮助文档

也就是说 -D 需要在 -jar 前面,所以正确读取 configs 目录下配置文件的命令应该是

java -Dspring.config.location=configs/application.yml -jar spring-boot-external-config-1.0-SNAPSHOT.jar

执行结果如下

正确加载外部目录的方式

总结

  1. 常用加载外部配置文件的命令参数

    java -Dspring.config.location=configs/application.yml -jar spring-boot-external-config-1.0-SNAPSHOT.jar

    java -jar spring-boot-external-config-1.0-SNAPSHOT.jar --spring.config.location=configs/application.yml

    -D 紧随 java 之后,在 -jar 之前,是 JVM 参数;-- 在 jar 名称之后,是 Spring Boot 命令行参数。两种方式有其各自的顺序写法,切勿张冠李戴!

  2. 推荐做法

    在 jar 包所在目录创建 config 文件夹,将外部配置文件置于该 config 文件夹下,启动的时候可以减少启动参数,那么出错的概率就会降低