如下代码,查找注册JavaScript脚本引擎,打印"Hello!",JavaScript脚本中的println是Rhino引擎额外提供的方法。
public class BasicScripting {
public void greet() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
//ScriptEngine engine = manager.getEngineByExtension("js");
//ScriptEngine engine = manager.getEngineByMimeType("text/javascript");
if (engine == null) {
throw new RuntimeException("找不到JavaScript语言执行引擎。");
}
engine.eval("println('Hello!');");
}
public static void main(String[] args) {
try {
new BasicScripting().greet();
} catch (ScriptException ex) {
Logger.getLogger(BasicScripting.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
使用默认的语言绑定对象:
public void useDefaultBinding() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
engine.put("name", "Alex");
engine.eval("var message = 'Hello, ' + name;");
engine.eval("println(message);");
Object obj = engine.get("message");
System.out.println(obj);
}
public void useCustomBinding() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
Bindings bindings = new SimpleBindings();
bindings.put("hobby", "playing games");
engine.eval("println('I like ' + hobby);", bindings);
}
默认情况下,脚本输入输出都是在标准控制台中,可以通过setReader和setWriter方法对输出流进行重定向,可以通过setErrorWriter方法进行错误输出重定向。
//例:将输出重定向到文件
public void scriptToFile() throws IOException, ScriptException {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setWriter(new FileWriter("output.txt"));
engine.eval("println('Hello World!');");
}
上下文中通过setAttribute和getAttribute方法获取和设置属性,类似于ServletContext中设置和获取属性操作。与ServletContext中不同的是,ScriptContext中的属性是有作用域之分的,ScriptContext按不同的顺序在不同的作用域中进行属性查找(类似于JSP中EL表达式属性的作用域)。通过ScriptContext的getScopes可以得到其中所有可用的作用域,其中预定义了两个作用域:常量ScriptContext.ENGINE_SCOPE(当前的脚本引擎)和ScriptContext.GLOBAL_SCOPE(从同一引擎工厂中创建的所有脚本引擎对象)。
public void scriptContextAttribute() {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setAttribute("name", "Alex", ScriptContext.GLOBAL_SCOPE);
context.setAttribute("name", "Bob", ScriptContext.ENGINE_SCOPE);
context.getAttribute("name"); //值为Bob
}
语言绑定对象位于ScriptContext中,同样也有作用域之分,范围越小,优先级越高。执行如下代码,输出的name值为Bob。
public void scriptContextBindings() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
Bindings bindings1 = engine.createBindings();
bindings1.put("name", "Alex");
context.setBindings(bindings1, ScriptContext.GLOBAL_SCOPE);
Bindings bindings2 = engine.createBindings();
bindings2.put("name", "Bob");
context.setBindings(bindings2, ScriptContext.ENGINE_SCOPE);
engine.eval("println(name);"); //Bob
}
public void useScriptContextValues() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("name", "Alex");
engine.eval("println(name);");
}
public void attributeInBindings() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setAttribute("name", "Alex", ScriptContext.GLOBAL_SCOPE);
engine.eval("println(name);");
}
4. 脚本编译
脚本语言一般均是解释执行的,相对于编译执行的语言,效率较低一些。当脚本语言需要多次重复执行时,可以先对煎熬本进行编译,避免重复解析,提高效率(注:脚本编译需要脚本引擎支持,实现javax.script.Compilable接口)。JavaSE中自带的JavaScript引擎是支持对脚本进行编译的,编译的脚本用javax.script.CompiledScript来表示。
public class ScriptCompile extends JsScriptRunner {
//对脚本进行编译
public CompiledScript compile(String scriptText) throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
if (engine instanceof Compilable) {
CompiledScript script = ((Compilable) engine).compile(scriptText);
return script;
}
return null;
}
//先编译再执行
public void run(String scriptText) throws ScriptException {
CompiledScript script = compile(scriptText);
if (script == null) {
return;
}
for (int i = 0; i < 100; i++) {
script.eval();
}
}
public static void main(String[] args) {
ScriptCompile sc = new ScriptCompile();
try {
sc.run("println('Hello');");
} catch (ScriptException ex) {
Logger.getLogger(ScriptCompile.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
① 在Java中调用脚本中的顶层方法
public void invokeFunction() throws ScriptException, NoSuchMethodException {
ScriptEngine engine = getJavaScriptEngine();
String scriptText = "function greet(name) { println('Hello, ' + name); } ";
engine.eval(scriptText);
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("greet", "Alex");
}
public void invokeMethod() throws ScriptException, NoSuchMethodException {
ScriptEngine engine = getJavaScriptEngine();
String scriptText = "var obj = { getGreeting : function(name) { return 'Hello, ' + name; } }; ";
engine.eval(scriptText);
Invocable invocable = (Invocable) engine;
Object scope = engine.get("obj");
Object result = invocable.invokeMethod(scope, "getGreeting", "Alex"); //第一个参数为方法所属对象
System.out.println(result);
}
Greet是Java实现的接口,包含一个方法getGreeting,通过Invocable.getInterface()方法指定脚本中的方法为Java接口的实现。
public void useInterface() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
String scriptText = "function getGreeting(name) { return 'Hello, ' + name; } ";
engine.eval(scriptText);
Invocable invocable = (Invocable) engine;
Greet greet = invocable.getInterface(Greet.class);
System.out.println(greet.getGreeting("Alex"));
}