IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> AJ 组件库之 支持热加载的配置器 EasyConfig -> 正文阅读

[Java知识库]AJ 组件库之 支持热加载的配置器 EasyConfig

简介

无论 xml 还是 yml 配置都归属 Spring 管理的,每次修改配置要重启服务器才能生效,于是思考怎么做一个不重启服务器的实现。实际上我之前都有探索过,参见我之前的博文:

平心而论,我之前的实现写得并不好,做的方法有些奇怪。好了,现在累积之前的坑和经验,重新再写,——这次写就是顺畅多了,一气呵成,代码也简单明了。

目前的实现就一个类,我们管他叫 EasyConfig

用法

Spring 注入这个组件,支持带构造器参数和不带的,参数就是配置 JSON 文件的磁盘路径。

<bean class="com.ajaxjs.util.config.EasyConfig"  />

或:

<bean class="com.ajaxjs.util.config.EasyConfig" >
	<constructor-arg index="0" value="config file path" /> 
</bean>

不带的构造器参数,默认是 classpath 下的 config.json 文件。

用法如下:

@Autowired
private EasyConfig config;
	
config.load(); // 加载配置

//@formatter:off  
config.save("{\r\n" +  // 保存配置
		"	\"clientShortName\": \"TEST\",\r\n" + 
		"	\"FOO\": {\r\n" + 
		"		\"NUMBER\": 1221,\r\n" + 
		"		\"STR\": \"BAR22\",\r\n" + 
		"		\"BOOLEAN\": true,\r\n" + 
		"		\"NULL\": null,\r\n" + 
		"		\"ARRAY\": [\r\n" + 
		"			1,\r\n" + 
		"			\"STR\",\r\n" + 
		"			null\r\n" + 
		"		]\r\n" + 
		"	}\r\n" + 
		"}");
//@formatter:on

assertEquals("BAR22", config.getStr("FOO.STR")); // 读取

根据不同配置类型,有下面获取方法。

/**
	* 读取配置并转换其为字符串类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	* 
	* @param key 配置键值
	* @return 配置内容
	*/
public String getStr(String key);

/**
	* 读取配置并转换其为 布尔 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	* 
	* @param key 配置键值
	* @return 配置内容
	*/
public boolean getBol(String key);

/**
	* 读取配置并转换其为 int 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	* 
	* @param key 配置键值
	* @return 配置内容
	*/
public int getInt(String key);

/**
	* 读取配置并转换其为 long 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	* 
	* @param key 配置键值
	* @return 配置内容
	*/
public long getLong(String key);

原理

先贴一下这个类的完整代码。

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import com.ajaxjs.util.io.FileHelper;
import com.ajaxjs.util.io.Resources;
import com.ajaxjs.util.logger.LogHelper;
import com.ajaxjs.util.map.JsonHelper;
import com.ajaxjs.util.map.ListMap;

/**
 * 不需要重启服务器的配置
 * 
 * @author sp42 frank@ajaxjs.com
 *
 */
public class EasyConfig extends HashMap<String, Object> {
	private static final LogHelper LOGGER = LogHelper.getLog(EasyConfig.class);

	private static final long serialVersionUID = 9099886055914666662L;

	/**
	 * 配置文件路径
	 */
	private String filePath;

	/**
	 * 创建一个配置器
	 * 
	 * @param filePath 配置文件路径
	 */
	public EasyConfig(String filePath) {
		super();
		this.filePath = filePath;
	}

	/**
	 * 创建一个配置器
	 */
	public EasyConfig() {
		super();
		filePath = Resources.getResourcesFromClasspath("config.json");
	}

	/**
	 * 加载 JSON 配置
	 */
	public void load() {
		if (!new File(filePath).exists()) {
			LOGGER.info("没有[{0}]项目配置文件", filePath);
			return;
		}

		loaded = false;
		String jsonStr = FileHelper.openAsText(filePath);
		Map<String, Object> map = JsonHelper.parseMap(jsonStr);

		clear();
		putAll(ListMap.flatMap(map));
		loaded = true;

		LOGGER.infoGreen("加载[" + get("clientShortName") + "]项目配置成功!All config loaded.");
	}

	/**
	 * 保存配置
	 * 
	 * @param jsonStr
	 */
	public void save(String jsonStr) {
		FileHelper.saveText(filePath, jsonStr);
		load();
	}

	/**
	 * 是否加载成功
	 */
	private boolean loaded;

	/**
	 * 内部的获取方法
	 * 
	 * @param <T>         配置类型
	 * @param key         配置键值
	 * @param isNullValue 当配置为 null 时返回的值,相当于“默认值”
	 * @param vType       配置类型的引用
	 * @return 配置内容
	 */
	@SuppressWarnings("unchecked")
	private <T> T getAny(String key, T isNullValue, Class<T> vType) {
		if (!loaded) {
			LOGGER.warning("配置系统未准备好");
			return isNullValue;
		}

		Object v = get(key);

		if (v == null) {
			LOGGER.warning("没发现[{0}]配置", key);
			return isNullValue;
		}

		return (T) v;
	}

	/**
	 * 读取配置并转换其为字符串类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	 * 
	 * @param key 配置键值
	 * @return 配置内容
	 */
	public String getStr(String key) {
		return getAny(key, null, String.class);
	}

	/**
	 * 读取配置并转换其为 布尔 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	 * 
	 * @param key 配置键值
	 * @return 配置内容
	 */
	public boolean getBol(String key) {
		return getAny(key, false, boolean.class);
	}

	/**
	 * 读取配置并转换其为 int 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	 * 
	 * @param key 配置键值
	 * @return 配置内容
	 */
	public int getInt(String key) {
		return getAny(key, 0, int.class);
	}

	/**
	 * 读取配置并转换其为 long 类型。仅对扁平化后的配置有效,所以参数必须是扁平化的 aaa.bbb.ccc 格式。
	 * 
	 * @param key 配置键值
	 * @return 配置内容
	 */
	public long getLong(String key) {
		return getAny(key, 0L, long.class);
	}

	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
}

所为读取配置就是不断读取一个 Map 的 Key/Value。Key 为参数,Value 为返回的结果。这个类本身继承 HashMap,序列化为 JSON 保存,读取这个 JSON,是一个带结构的 Map(一棵“树”)。

在这个 Map 上,get("xx1").get("xx2").get("xx3")?——那样太傻。或者 get("xx1.xx2.xx3")?——那样不错,不过持久化也是那样吗?如:

{
	“xx1.xx2.xx1”: 1,
	“xx1.xx2.xx2”: 2,
	“xx1.xx2.xx3”: 3,
}

Key 重复得厉害,一改就得多处修改,——还是原来带层次的 JSON 结构好(一棵“树”),所以得把这个树“扁平化”处理。具体就是这个函数,原理都是比较基础的数据结构知识。

/**
 * 扁平化多层 map 为单层
 * 
 * @param map 多层 Map
 * @return 单层 Map
 */
public static Map<String, Object> flatMap(Map<String, Object> map) {
	final Stack<String> stack = new Stack<>();
	final Map<String, Object> _map = new HashMap<>();

	ListMapConfig config = new ListMapConfig();
	config.newKey = key -> stack.add(key); // 进栈
	config.exitKey = key -> stack.pop(); // 退栈
	config.mapEntryHandler = (key, obj, currentMap, superMap, i) -> {
		if (obj == null || obj instanceof String || obj instanceof Number || obj instanceof Boolean) {
			StringBuilder sb = new StringBuilder();

			for (String s : stack)
				sb.append(s + ".");

			_map.put(sb + key, obj);
		}

		return true;
	};

	traveler(map, config);

	return _map;
}

这个类还调用了其他工具方法,很简单无非读取文件之类的,如果需要可以问我索取。

小结

这个配置器并不是要代替 Spring 的 properties/xml/yml 文件,其最大特点就是支持热加载,修改后程序马上生效。至于前端方面应该有个比较好的、通用的 JSON 修改器,下一期我们再讲。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:35:37  更:2022-03-21 20:39:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:54:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码