想要在Java中运行脚本语言,需要运行该脚本语言的特定的引擎,而JavaScript的引擎则是Nashorm,该引擎是jdk8默认集成的,可以直接使用。
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// 运行一个字符串脚本
String scriptString = "n = 3";
engine.eval(scriptString);
// 运行存储在文本文件中的脚本
BufferedReader reader = Files.newBufferedReader(Paths.get("test.txt"));
engine.eval(reader);
// 在同一个引擎上面运行过的脚本会保留到最后,比如这里使用了刚才定义的 n 参数
Object result = engine.eval("n + 1");
System.out.println("结果为 4 --------》 result = " + result);
对于Nashorm引擎,多线程是不安全的,不要在多线程的环境下使用。可以通过下面的方式查询一个引擎是否支持多线程环境。
-
null 不支持并发执行
-
MULTITHREADED 支持并发执行,多个线程之间效果可能可见
-
THREAD-ISOLATED 支持并发执行,为每一个线程创建独立的变量,不互相影响
-
STATELESS 支持并发执行,不创建独立变量,互相影响
System.out.println("是否支持多线程:" + engine.getFactory().getParameter("THREADING"));
关于变量的域,最大的是全局的域,添加到ScriptEngineManager中,对所有的引擎可见。比较常用的是引擎的域,就是一个引擎中可见。一个引擎中还可以继续细分成多个Binding域。
manager.put("param", 100);
engine.put("param", 80);
System.out.println("引擎域" + engine.get("param"));
Bindings bindings = engine.createBindings();
bindings.put("param", 60);
System.out.println("结果为binding的变量+1 -------> " + engine.eval("param + 1", bindings));
可以改变引擎的源输出流,把结果打印到其他地方,比如本地文件里面。
String outScript = "print(\"Hello\")";
BufferedWriter writer = Files.newBufferedWriter(Paths.get("out.txt"));
engine.getContext().setWriter(writer);
engine.eval(outScript);
nashorn引擎实现了Invocable接口,可以直接调用函数,调用的方式如下:
String funScript = "function sayHello(person){ return 'hello ' + person }";
engine.eval(funScript);
Object funResult = ((Invocable) engine).invokeFunction("sayHello", "zhangsan");
System.out.println(funResult);
调用对象中的函数:
String defineObj = "function Person(name){ this.name = name }";
String defineMethod =
"Person.prototype.sayHello = function(other)" +
"{ return this.name + ' say hello to ' + other }";
engine.eval(defineObj);
engine.eval(defineMethod);
Object obj = engine.eval("new Person('lisi')");
Object objResult = ((Invocable) engine).invokeMethod(obj, "sayHello", "zhangsan");
System.out.println(objResult);
使用脚本语言实现自己的接口也是可以的,接口的方法名称和脚本语言中定义的函数名称相同:
// 定义一个接口
public interface Person {
String sayHello(String person);
}
------------------------------------
String funScript = "function sayHello(person){ return 'hello ' + person }";
engine.eval(funScript);
Person person = ((Invocable) engine).getInterface(Person.class);
System.out.println(person.sayHello("zhangsanJava"));
如果需要重复的运行一段脚本,可以把它编译成中间状态,然后就可以高效地执行脚本:
String funScript = "n + 1";
CompiledScript script = ((Compilable) engine).compile(funScript);
if (null != script) {
script.eval();
} else {
engine.eval(funScript);
}