在 Spring Boot 3.5 项目中,常常会遇到多层配置(默认配置、占位符配置、Profile 配置、外部配置、配置中心)叠加的场景。本文记录了我在实际项目中遇到的加密属性解密问题、配置覆盖顺序以及解决方案,分享对 EnvironmentPostProcessor 和 Spring Boot 配置加载顺序的理解。
通过本文,你可以了解如何在保证配置优先级正确的前提下,实现加密属性的安全解密,并避免多层配置冲突导致的运行时问题。
在项目中,我们使用 Spring Boot 3.5,配置分为多层:
common.properties,提供基础默认值placeholder-keys.properties,用于占位符引用,如 ${ph.mysql.host}application-dev.properties 等,覆盖本地环境特有配置ExternalConfigProcessor 加载 /data/config/config.properties 等外部文件此外,一些属性是加密的(例如数据库密码),格式为 dec()xxxx,需要在启动时解密。
在引入 ExternalConfigProcessor 和 DecryptEnvironmentPostProcessor 后,项目出现了 JDBC 连接异常:
spring.datasource.url 中的 host 不是 profile 配置的值,也不是默认的本地值spring.datasource.username 和 password 已成功解密addFirst,破坏了 PropertySource 原有顺序,导致占位符解析错误Spring Boot 3.5 的配置加载顺序(优先级从低到高):
SpringApplication.setDefaultProperties)application.properties / application.ymlspring.config.import 本地文件(按顺序加入,优先级低于 profile)application-{profile}.properties / .yml)ExternalConfigProcessor.addFirst)注意:PropertySource 在 Environment 中的顺序决定了占位符
${...}的解析顺序,最前面的 PropertySource 优先。
addFirst 将解密后的属性放到最前面核心思想:只替换加密属性,保持 PropertySource 顺序不变。
示例实现:
private void replace(MutablePropertySources propertySources, MapPropertySource ps) {Map<String, Object> newSource = new HashMap<>(ps.getSource());boolean isReplaced = false;for (String name : ps.getPropertyNames()) {Object value = ps.getProperty(name);if (value != null) {String v = String.valueOf(value);if (v.length() <= decryptPrefixLength || !v.startsWith(decryptPrefix)) {continue;}isReplaced = true;v = v.substring(decryptPrefixLength);v = enhance.decode(v);newSource.put(name, v);LOGGER.debug("{} decrypt success !", name);}}if (isReplaced) {propertySources.replace(ps.getName(), new MapPropertySource(ps.getName(), newSource));}}
addFirst 的顺序影响最终优先级addFirst → 优先级最高orderreplace 覆盖远程配置中的同名属性| 配置类型 | PropertySource 添加位置 | 覆盖规则 |
|---|---|---|
| 默认属性 | Environment 初始 | 最低优先级 |
| application.properties | 后加入 | 可被 imports/profile/外部/远程覆盖 |
| spring.config.import 本地文件 | addLast | 后面文件覆盖前面文件,同名属性被覆盖,优先级低于 profile |
| profile 文件 | addLast | 覆盖 imports / defaults |
| 外部配置文件 | addFirst | 覆盖 profile/import/default,但低于远程配置(按 addFirst 最终顺序) |
| 配置中心 | addFirst | 覆盖本地所有配置,优先级高 |
| 命令行参数 | addFirst / SpringApplication 默认 | 最高优先级 |
注意:
replace可以在不破坏顺序的前提下修改 PropertySource 的部分属性。
order + addFirst/addLast/replace 决定最终覆盖关系replace,只替换加密字段,不破坏原有 PropertySource 顺序${...} 解析顺序必须结合 PropertySource 优先级理解DecryptEnvironmentPostProcessor.java
LcxmCommonPropertiesPostProcessor.java
[spring.factories](