0
点赞
收藏
分享

微信扫一扫

Java 通用配置(二)JVM和环境变量实现


Java 通用配置
​​​(一)设计​​ (二)JVM和环境变量实现

本系列参考实现:

​​https://gitee.com/mybatis-mapper/config​​​​https://github.com/mybatis-mapper/config​​

在写系列博客时,总有一两篇内容简单到可有可无…

本篇内容先选择了最简单的 JVM 和环境变量进行实现,实现过程中可以了解一个简单的规则,SPI 的配置等等,算是复杂实现前的开胃小菜。

Java 通用配置(二)JVM和环境变量实现_环境变量

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​​ 尝试获取,如果能拿到,就不需要做处理,如果没有再按下面规则转换:

  1. 转换为大写形式
  2. 替换​​.​​​为下划线​​_​
  3. 去掉所有​​-​

这里参考的 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参数如下:

Java 通用配置(二)JVM和环境变量实现_环境变量_02

-Dhello=JVM你好 -Denabled=true

再次运行,输出如下内容:

JVM你好
true

删除JVM参数后,配置环境变量如下(IDEA启动后,直接配置系统环境变量是无效的,需要完全关闭IDEA启动才可能有效,可以直接通过IDEA提供的环境变量方式进行配置):

Java 通用配置(二)JVM和环境变量实现_ide_03

hello=ENV你好;ENABLED=true

输出内容如下:

ENV你好
true

接下来同时配置 JVM 和环境变量:

Java 通用配置(二)JVM和环境变量实现_jvm_04


此时的输出结果如下:

JVM你好
true

可以看到 JVM 优先级高于环境变量。

本篇内容很简单,如果你对 SPI 不是很熟悉,可以搜索相关的文章进行了解,如果你们现有系统想支持这两种扩展方式,可以参考这里的实现在自己系统中增加这两种读取参数的方式。在后续的文章中会开始介绍用户自定义配置和版本配置,因为涉及到读取文件,相对复杂了很多,难度增加了不少,不好理解时多动手试试。


举报

相关推荐

0 条评论