Java 通用配置JVM和环境变量实现
Posted isea533
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 通用配置JVM和环境变量实现相关的知识,希望对你有一定的参考价值。
Java 通用配置
(一)设计
(二)JVM和环境变量实现
本系列参考实现:
https://gitee.com/mybatis-mapper/config
https://github.com/mybatis-mapper/config
在写系列博客时,总有一两篇内容简单到可有可无…
本篇内容先选择了最简单的 JVM 和环境变量进行实现,实现过程中可以了解一个简单的规则,SPI 的配置等等,算是复杂实现前的开胃小菜。
JVM 参数
直接实现前面定义的 Config
接口就可以,系统变量太简单,以至于没有内容可讲:
package io.mybatis.config.defaults;
import io.mybatis.config.Config;
/**
* 读取环境变量值
*
* @author liuzh
*/
public class SystemConfig implements Config
@Override
public String getStr(String key)
return System.getProperty(key);
@Override
public int getOrder()
return SYSTEM_ORDER;
系统变量 JVM 参数方式是目前优先级最高的配置方式,代码中可以看到是 System.getProperty
方式读取的,所以在代码中可以通过 System.setProperty("key", "value");
进行设置,只要在 getProperty
前设置了,读取都会有效。
除了代码方式外,JVM 真正常用的地方还是在执行代码时,例如 Spring Boot 可执行 Jar 包运行时:
java -Dkey=value -jar xxxx-boot.jar
假如我们通过系统服务的方式运行 java 程序,通过JVM设置的参数是比较死板的,只是相对配置文件方便了一些,如果是在容器环境运行,由于优先级顺序很高,这种方式还会导不能通过环境变量进行覆盖,所以JVM覆盖值的用法在云原生环境使用较少。
环境变量
实现比 JVM 复杂了一点点,代码如下:
package io.mybatis.config.defaults;
import io.mybatis.config.Config;
/**
* 读取环境变量值
*
* @author liuzh
*/
public class EnvConfig implements Config
@Override
public String getStr(String key)
String value = System.getenv(key);
if (value != null)
return value;
key = key.toUpperCase().replaceAll("\\\\.", "_").replaceAll("-", "");
return System.getenv(key);
@Override
public int getOrder()
return ENV_ORDER;
由于不同操作系统对环境变量有不同的限制或规范,从环境变量取值时一般都先通过原始 key
尝试获取,如果能拿到,就不需要做处理,如果没有再按下面规则转换:
- 转换为大写形式
- 替换
.
为下划线_
- 去掉所有
-
这里参考的 Spring 规则
在云原生(使用容器镜像或K8s)中,因为配置环境变量非常方便,所以非常有必要支持,而且推荐通过环境变量进行覆盖。
如果你用到了配置中心,可以优先配置中心方式。
在 K8s 中用 ConfigMap 或者挂载 ConfigMap 卷覆盖配置文件都是很方便的选择。
JAVA SPI 配置
在 src/main/resources
资源目录下面创建 META-INF/services
目录,在新建的目录下面创建 io.mybatis.config.Config
文件,文件名就是接口名,在文件中配置上面两个实现:
io.mybatis.config.defaults.EnvConfig
io.mybatis.config.defaults.SystemConfig
JAVA SPI 初始化
在 ConfigHelper
中通过 ServiceLoader.load(Config.class)
初始化了 Config
接口的实现:
private static void init()
if (CONFIGS == null)
synchronized (ConfigHelper.class)
if (CONFIGS == null)
CONFIGS = new ArrayList<>();
ServiceLoader<Config> serviceLoader = ServiceLoader.load(Config.class);
for (Config config : serviceLoader)
CONFIGS.add(config);
CONFIGS.sort(Comparator.comparing(Config::getOrder).reversed());
CONFIGS.forEach(c -> log.debug("加载配置类: " + c.getClass().getName()));
这里通过 双重检查锁(Double-Checked Locking)对 private static volatile List<Config> CONFIGS;
进行初始化。
获取所有 Config
实现后,根据 Comparator.comparing(Config::getOrder).reversed()
进行倒序排序,所以这里的数字越大优先级越高。
简单测试
测试代码如下:
public class MainTest
public static void main(String[] args)
System.out.println(ConfigHelper.getStr("hello"));
System.out.println(ConfigHelper.getBoolean("enabled"));
直接用 main
方法测试,不做任何配置的情况下,运行上面代码输出如下:
null
false
在IDE中配置JVM参数如下:
-Dhello=JVM你好 -Denabled=true
再次运行,输出如下内容:
JVM你好
true
删除JVM参数后,配置环境变量如下(IDEA启动后,直接配置系统环境变量是无效的,需要完全关闭IDEA启动才可能有效,可以直接通过IDEA提供的环境变量方式进行配置):
hello=ENV你好;ENABLED=true
输出内容如下:
ENV你好
true
接下来同时配置 JVM 和环境变量:
此时的输出结果如下:
JVM你好
true
可以看到 JVM 优先级高于环境变量。
本篇内容很简单,如果你对 SPI 不是很熟悉,可以搜索相关的文章进行了解,如果你们现有系统想支持这两种扩展方式,可以参考这里的实现在自己系统中增加这两种读取参数的方式。在后续的文章中会开始介绍用户自定义配置和版本配置,因为涉及到读取文件,相对复杂了很多,难度增加了不少,不好理解时多动手试试。
以上是关于Java 通用配置JVM和环境变量实现的主要内容,如果未能解决你的问题,请参考以下文章