前言
由于一些需求,现在需要在Java中解析字符串,做一些简单的算数运算和逻辑运算,那么最先想的是模板引擎这个东西,但是Java中的模板引擎是针对View层的,也就是JSP的,在Service层中使用不是太方便,因此选用了原生的JavaScript脚本解析引擎。实际上Java原生支持解析大部分脚本语言,像JavaScript,PHP,Python等。
那么,先贴一下核心实现类的代码:
import java.io.FileNotFoundException;
import java.util.Map;
import javax.script.ScriptException;
public interface TempletEngineService {
public Object eval(String script) throws ScriptException, FileNotFoundException;
public Object eval(String script, Map<String, Object> vars) throws ScriptException, FileNotFoundException;
}
然后是实现类:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
@Service
public class TempletEngineServiceImpl implements TempletEngineService {
private ScriptEngine jsEngine;
public TempletEngineServiceImpl() {
ScriptEngineManager manager = new ScriptEngineManager();
jsEngine = manager.getEngineByName("JavaScript");
}
@Override
public Object eval(String script) throws ScriptException, FileNotFoundException {
if (script == null || script.equals("")) {
return null;
}
String jsfile = "/home/phw/Workspaces/MyEclipseProjects/gomoo-fu/src/com/gomoo/util/script/template.js";
jsEngine.eval(new FileReader(new File(jsfile)));
return jsEngine.eval(script);
}
@Override
public Object eval(String script, Map<String, Object> vars) throws ScriptException, FileNotFoundException {
if (script == null || script.equals("")) {
return null;
}
if (vars == null || vars.isEmpty()) {
for (Map.Entry<String, Object> entry: vars.entrySet()) {
jsEngine.put(entry.getKey(), entry.getValue());
}
}
return eval(script);
}
}
主要的核心类就是这两个文件,可以看到,用到的解析引擎是javax.script包下的引擎。
脚本是自定义的一串符合JavaScript规则的字符串,像简单点的字符串“5 + 6”、"9 - (6 + 5) * 5"等等,可以直接执行eval函数;若需要计算一些复杂的逻辑,那可以写到你自定义的一个js文件里,然后在脚本中运行那个函数;那再复杂一点,在函数中或者脚本中带自定义参数,这时就用到了eval(String script, Map vars)函数了。
下面是一些例子:
/**
* 简单字符串的测试
* @throws ScriptException
* @throws FileNotFoundException
*/
@Test
public void testEval() throws FileNotFoundException, ScriptException {
String script = "(65 - 5) / (3 * 6)";
String script2 = "15 > 10 ? 15 : 10";
String script3 = "15 > 10";
Object val1 = templetEngineService.eval(script);
System.out.println("脚本1的结果为:" + val1);
Object val2 = templetEngineService.eval(script2);
System.out.println("脚本2的结果为:" + val2);
Object val3 = templetEngineService.eval(script3);
System.out.println("脚本3的结果为:" + val3);
}
/**
* 脚本1的结果为:3.3333333333333335
* 脚本2的结果为:15
* 脚本3的结果为:true
*/
/**
* 带函数的脚本测试
* @throws ScriptException
* @throws FileNotFoundException
*/
@Test
public void testEval2() throws FileNotFoundException, ScriptException {
String script = "random()";
String script2 = "random() + random()";
Object val1 = templetEngineService.eval(script);
System.out.println("脚本1的结果为:" + val1);
Object val2 = templetEngineService.eval(script2);
System.out.println("脚本2的结果为:" + val2);
}
/**
* 脚本1的结果为:63.0
* 脚本2的结果为:163.0
*/
/**
* 带参数的脚本测试
* @throws FileNotFoundException
* @throws ScriptException
*/
@Test
public void testEval3() throws FileNotFoundException, ScriptException {
String script = "random(min, max)";
String script2 = "random(min, max) + min - max";
Map<String, Object> vars = new HashMap<>();
vars.put("min", 10);
vars.put("max", 100);
Object val1 = templetEngineService.eval(script, vars);
System.out.println("脚本1的结果为:" + val1);
Map<String, Object> vars2 = new HashMap<>();
vars.put("min", 100);
vars.put("max", 1000);
Object val2 = templetEngineService.eval(script2, vars2);
System.out.println("脚本2的结果为:" + val2);
}
/**
* 脚本1的结果为:69.0
* 脚本2的结果为:-65.0
*/
/**
* 参数为类时的脚本测试
* @throws FileNotFoundException
* @throws ScriptException
*/
@Test
public void testEval4() throws FileNotFoundException, ScriptException {
User user = new User();
user.setRealName("phw");
String script = "out(user)";
Map<String, Object> vars = new HashMap<>();
vars.put("user", user);
Object val = templetEngineService.eval(script, vars);
System.out.println("脚本1的结果为:" + val);
}
/**
* 脚本1的期望结果:phw
*/
下面贴一下template.js,也就是我们预定义的Js function的文件:
/**
* 生成100内的随机数
*/
function random() {
return Math.floor(Math.random() * 100);
}
/**
* 生成区间内的随机整数[m, n]
*/
function random(minNum,maxNum){
switch(arguments.length){
case 1:
return parseInt(Math.random()*minNum+1,10);
break;
case 2:
return parseInt(Math.random()*(maxNum-minNum+1)+minNum,10);
break;
default:
return 0;
break;
}
}
function out(obj) {
return obj.realName;
}
以上就是测试里用到的js function函数,再说一下,脚本不支持es6的语法,所以老老实实用var,不要用let。
以上。