寻求按指定优先级加载的Java扩展属性配置实现方案
Java扩展属性配置实现方案(按优先级覆盖)
嘿伙计,我之前刚好做过完全符合你需求的属性配置加载逻辑,优先级完全是你要的命令行参数 > Java系统属性 > 操作系统环境变量 > Jar包外的application.properties,高优先级配置会直接覆盖低优先级的,下面给你详细拆解实现思路和代码:
核心思路
咱们要按从低到高的顺序加载各类配置,每一步加载后都用当前级别的配置去覆盖之前已加载的低优先级配置,这样最后得到的就是优先级最高的有效配置。具体顺序是:
- 先加载Jar包外的
application.properties(最低优先级) - 再加载操作系统环境变量,转换键名格式后覆盖
- 接着加载Java系统属性,覆盖已有配置
- 最后加载命令行参数,覆盖所有前置配置(最高优先级)
具体实现代码
1. 工具类:PropertyLoader.java
这个类封装了所有加载逻辑,包含各个配置源的加载和优先级覆盖逻辑:
import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class PropertyLoader { // 环境变量转properties键名的正则:下划线分隔转点分隔,大写转小写 private static final Pattern ENV_VAR_PATTERN = Pattern.compile("_(.)"); public static Properties loadProperties(String[] args) throws IOException { Properties properties = new Properties(); // 1. 加载Jar包外的application.properties(默认当前目录) loadExternalProperties(properties); // 2. 加载操作系统环境变量,转换键名后覆盖 loadEnvVariables(properties); // 3. 加载Java系统属性,覆盖已有配置 loadSystemProperties(properties); // 4. 加载命令行参数,覆盖所有前置配置 loadCommandLineArgs(properties, args); return properties; } private static void loadExternalProperties(Properties properties) throws IOException { // 这里可以自定义路径,比如通过系统参数指定,这里默认当前目录下的application.properties String externalPropsPath = System.getProperty("external.props.path", "./application.properties"); try (FileInputStream fis = new FileInputStream(externalPropsPath)) { properties.load(fis); System.out.println("Loaded external application.properties successfully"); } catch (IOException e) { // 如果文件不存在,忽略(可选:可以抛出异常,根据需求调整) System.out.println("External application.properties not found, skipping"); } } private static void loadEnvVariables(Properties properties) { Map<String, String> envVars = System.getenv(); for (Map.Entry<String, String> entry : envVars.entrySet()) { // 转换环境变量键名:比如MY_APP_PORT → my.app.port String propKey = convertEnvVarToPropKey(entry.getKey()); String propValue = entry.getValue(); // 只有当值非空时才覆盖 if (propValue != null && !propValue.isEmpty()) { properties.setProperty(propKey, propValue); } } System.out.println("Loaded environment variables successfully"); } private static String convertEnvVarToPropKey(String envVarKey) { // 转小写后,把下划线+后续字符转成点+大写字符,最后统一转小写确保格式一致 String lowerCaseKey = envVarKey.toLowerCase(); Matcher matcher = ENV_VAR_PATTERN.matcher(lowerCaseKey); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, "." + matcher.group(1).toUpperCase()); } matcher.appendTail(sb); return sb.toString().toLowerCase(); } private static void loadSystemProperties(Properties properties) { // 可按需过滤特定前缀的系统属性,比如只加载以"app."开头的 Properties systemProps = System.getProperties(); for (String key : systemProps.stringPropertyNames()) { String value = systemProps.getProperty(key); if (value != null && !value.isEmpty()) { properties.setProperty(key, value); } } System.out.println("Loaded system properties successfully"); } private static void loadCommandLineArgs(Properties properties, String[] args) { // 处理--key=value格式的命令行参数,可按需扩展支持其他格式 if (args == null || args.length == 0) { return; } for (String arg : args) { if (arg.startsWith("--")) { String[] keyValue = arg.substring(2).split("=", 2); if (keyValue.length == 2) { String key = keyValue[0].trim(); String value = keyValue[1].trim(); if (!key.isEmpty() && !value.isEmpty()) { properties.setProperty(key, value); } } } } System.out.println("Loaded command line arguments successfully"); } }
2. 使用示例
在你的主类里调用这个工具类,获取最终的有效配置:
import java.util.Properties; public class MainApplication { public static void main(String[] args) { try { Properties finalProps = PropertyLoader.loadProperties(args); // 示例:获取配置值,带默认值兜底 String appPort = finalProps.getProperty("app.port", "8080"); String appName = finalProps.getProperty("app.name", "default-app"); System.out.println("Final effective configuration:"); System.out.println("app.port = " + appPort); System.out.println("app.name = " + appName); // 后续业务逻辑直接使用finalProps即可 } catch (Exception e) { e.printStackTrace(); } } }
关键细节说明
- 环境变量键名转换:适配环境变量(大写下划线)和properties(小写点分隔)的命名差异,确保同一个配置项能跨源识别。
- 空值保护:所有加载步骤仅在值非空时覆盖,避免高优先级的空值清空低优先级的有效配置。
- 灵活的外部配置路径:默认读取当前目录的
application.properties,也可通过-Dexternal.props.path=/custom/path/application.properties指定自定义路径。 - 可扩展的命令行格式:默认处理
--key=value,如果需要支持-key value等格式,只需修改loadCommandLineArgs的解析逻辑即可。
测试验证
你可以按以下步骤验证优先级:
- 在Jar外的
application.properties里设置app.port=8080 - 系统环境变量设置
MY_APP_PORT=8081 - 启动时添加系统参数
-Dapp.port=8082 - 启动时添加命令行参数
--app.port=8083
最终得到的app.port会是8083,完全符合优先级顺序。
内容的提问来源于stack exchange,提问作者Frido
相关产品推荐
相关产品推荐

