Java Web:JSON 作为配置文件,简单读写的方法

本方法依赖于 JRE 自带的 JavaScript 引擎 Rhino,无须其他第三方 JAR 包。更多这方面的资料,参见《学习使用 Java 自带的 JS 引擎》《使用自带的 Rhino 作为 Java 的 JSON 解析包》

具体流程参见源码:http://code.taobao.org/p/bigfoot_v2/src/java/com/ajaxjs/framework/javascript/JSON_Saver.java

读取配置文件

先大概说一说思路。首先配置文件以 *.json 格式保存在服务端磁盘上。要读取改配置文件的话,通过 java.io.File 包读取磁盘内容,然后形成接口,作为响应内容返回到客户。既然 Web 浏览器天然支持 JSON,这读取一过程我们借助 <script src="xxx.json"></script> 即可。得到 JSON 数据后,再通过 JavaScript 绑定到 HTML 表单上。

读取 JSON 文件内容很简单,我的代码如下:

String filePath = new RequestHelper(request).Mappath("/META-INF/site_config.js");
out.println(Fso.readFile(filePath));

RequestHelper.Mappath 和 Fso.readFile 都是我封装的函数,分别是把虚拟的相对路径还原为磁盘的绝对路径;Fso.readFile 顾名思义是读取文件内容。你可以将此写成 JSP 或 Servlet,让浏览器通过 <script src="xxx.jsp"></script> 访问得到即可。另外这里我还加入了格式化函数(Formatter),让输出的 JSON 带有缩进,更方便调试和阅读。

<script type="text/framework/javascript" src="?getConfig"></script>
如上例,我是设置当前 JSP 得到 JSON 内容的,效果如下图。

Java Web:JSON 作为配置文件,简单读写的方法

Java Web:JSON 作为配置文件,简单读写的方法

注意我们把 JSON 显示到表单是通过下面简单的函数的:

// 数据绑定
// v1.00
function databing(data){
	for(var i in data){
		var el = document.querySelector('*[name="bf_Config.site.{0}"]'.format(i));
		el.value = data[i];
	}
}
databing(bf_Config.site);
这里表单各个输入控件的 name 属性是有讲究的,命名方式都是以 JSON 的中 JSON Path 完整路径为依据,如这里的 bf_Config.site.{0}。一个控件项对应一个 JSON 节点。

表单代码如下:

<form class="form-horizontal" method="post" action="?">
	<input type="hidden" name="jsonFile" value="/META-INF/site_config.js" />
	<input type="hidden" name="topVarName" value="bf_Config" />
	<div class="control-group">
		<label class="control-label">网站标题前缀</label>
		<div class="controls">
			<input type="text" name="bf_Config.site.titlePrefix" class="ui-wizard-content"
				placeholder="请输入旧密码" errmsg="旧密码为必填项" requiredfield />
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">搜索关键字</label>
		<div class="controls">
			<textarea name="bf_Config.site.keywords" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">网站描述</label>
		<div class="controls">
			<textarea name="bf_Config.site.description" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="control-group">
		<label class="control-label">底部版权<br />声明文字
		</label>
		<div class="controls">
			<textarea name="bf_Config.site.footCopyright" rows="10"></textarea>
			<span class="requiredfield">*</span>
		</div>
	</div>
	<div class="form-actions">
		<button class="btn btn-success submitBtn">保存</button>
	</div>
</form>

生成表单界面如下:

Java Web:JSON 作为配置文件,简单读写的方法

其中要注意两处隐藏域:

<input type="hidden" name="jsonFile" value="/META-INF/site_config.js" />
<input type="hidden" name="topVarName" value="bf_Config" />
一个说明是哪个 JSON 文件要被保存的,一个是 JSON 的*节点名。

保存配置内容

点击“保存”按钮之后,表单提交如下数据到后台。

Java Web:JSON 作为配置文件,简单读写的方法

具体流程参见源码:http://code.taobao.org/p/bigfoot_v2/src/java/com/ajaxjs/framework/javascript/JSON_Saver.java

保存配置文件,一个是修改当前内存的配置信息,其次是将配置内存保存在服务端的磁盘上,所以说这里是两个主要的操作,因此也需要两个 JavaScript 运行时。一个是 BaseApplication.jsRuntime 静态类型的,常驻内存的,另外一个是用于当前请求的实例化的这么一个 JS 运行时,用于得到 JSON 序列化后的字符串。它们分别作用各不同。

以上逻辑都安排在 save(HttpServletRequest request) 方法:

private void save(HttpServletRequest request) throws Exception {
	Map<String, String> hash = RequestSender.getClient_Data(request);
	String jsonFileFullPath = load(request, hash);
	
	// 可以json.str() 的 变量名
	String topVarName = hash.get("topVarName");
	Util.isEmptyString(topVarName, "没有  topVarName 参数!");
	hash.remove("topVarName");
        saveRAM(hash);
	
	String JSON_as_String = null;
	try {
		JSON_as_String = (String)eval("JSON.stringify(" + topVarName + ");");
	} catch (ScriptException e) {
		e.printStackTrace();
		throw new Exception("更新配置失败,不能序列化配置!");
	}
	
	if(JSON_as_String != null){ // 持久化配置文件
		String fileBody = topVarName + " = " + JSON_as_String + ";";
//			System.out.println(fileBody);
//			System.out.println("::::::::::::::::::2:"+jsonFileFullPath);
		try {
			Fso.writeFile(jsonFileFullPath, fileBody);
		} catch (IOException e) {
			e.printStackTrace();
			throw new Exception("更新配置失败,不能保存配置!");
		}
	}
}

首先,我们需要把这些数据变为一个 Map:Map<String, String> hash = RequestSender.getClient_Data(request);,加载到内存中,然后对其修改(实际是覆盖过程,同时对两个 js runtime 皆有效),最后保存到文件中(Fso.write)。

/**
 * 写入内存,覆盖
 * @param hash
 */
private void save(Map<String, String> hash){
	String jsCode = "";
	
	for(String key : hash.keySet()){
		jsCode = key + " = '" + hash.get(key) + "';"; // 全部保存为 String。TODO 支持其他类型
		
//			System.out.println(jsCode);
		
		try{
			js.eval(jsCode); // 两个 js runtime
			eval(jsCode);
		}catch(ScriptException e) {
			System.err.println("写入内存,覆盖失败!");
			e.printStackTrace();
		}
	}
}

最后保存完毕,输出 JSON 结果:new ResponseHelper(response).outputJSON(request); 提示用户成功。应该说整个过程并不复杂,操作也足够直观。如果要说有什么地方没考虑到的,就是安全性了。实际使用中还需要注意权限,因为这是直接对服务端的文件进行写操作!

另外有一点可以优化的地方,那就是合并两个 js runtime 为一个,不知是否可行呢?

上一篇:微信支付id出现的重复支付解决方法和app应用中多种支付方式之间的对比


下一篇:【行业|分析】大数据对于银行七大应用