简要描述:其实在jdk1.6中就引入了支持脚本语言的API。这使得java能够很轻松的调用其他脚本语言。具体API的使用参考下面的代码:
1 package com.rampage.jdk7.chapter2; 2 3 import java.io.FileWriter; 4 import java.io.IOException; 5 6 import javax.script.Bindings; 7 import javax.script.Compilable; 8 import javax.script.CompiledScript; 9 import javax.script.Invocable; 10 import javax.script.ScriptContext; 11 import javax.script.ScriptEngine; 12 import javax.script.ScriptEngineManager; 13 import javax.script.ScriptException; 14 import javax.script.SimpleBindings; 15 16 /** 17 * 简单的脚本引擎使用方法 18 * 19 * @author zyq 20 * 21 */ 22 public class SimpleScriptAPI { 23 public static void main(String[] args) throws ScriptException, IOException, NoSuchMethodException { 24 SimpleScriptAPI simpleScriptEngine = new SimpleScriptAPI(); 25 26 // Part 1: 通用的脚本引擎用法 27 ScriptEngine engine = simpleScriptEngine.getJavaScriptEngine(); 28 if (engine == null) { 29 throw new RuntimeException("找不到JavaScript脚本执行引擎!"); 30 } 31 engine.eval("var a = 12;"); 32 33 // Part 2: 不同脚本语言与java之间的对象绑定 34 engine.put("name", "Alex"); 35 engine.eval("var message = 'hello ' + name"); 36 Object message = engine.get("message"); 37 System.out.println(message); // hello Alex 38 39 // 当然也可以通过SimpleBinds对象来进行变量绑定或者通过脚本引擎的createBindings方法 40 Bindings bindings = new SimpleBindings(); 41 bindings = engine.createBindings(); 42 bindings.put("hobby1", "java"); 43 bindings.put("hobby2", "dota"); 44 engine.eval("var message2 = 'I like ' + hobby1 + ' and ' + hobby2", bindings); 45 // 使用binding来绑定的变量只能在脚本语言内部是使用,java语言内获取不到对应的变量 46 System.out.println(engine.get("message2")); // null 47 System.out.println(engine.get("hobby1")); // null 48 System.out.println(engine.get("hobby2")); // null 49 engine.put("hobby1", "java"); 50 engine.put("hobby2", "dota"); 51 engine.eval("var message2 = 'I like ' + hobby1 + ' and ' + hobby2"); 52 System.out.println(engine.get("message2")); // I like java and dota 53 System.out.println(engine.get("hobby1")); // java 54 System.out.println(engine.get("hobby2")); // dota 55 56 // Part 3: 脚本执行上下文 57 // ScriptContext的setReader/setWriter/setErrorWriter可以分别设置脚本执行时候的输入来源,输出目的地和错误输出目的地 58 ScriptContext context = engine.getContext(); 59 context.setWriter(new FileWriter("output.txt")); 60 engine.eval("var a = 13"); 61 // ScriptContext中也有setAttribute和getAttribute方法来自定义属性。属性有不同的作用域之分。 62 // 每个作用域都以一个对应的整数表示其查找顺序,该整数越小,说明查找时的顺序更优先。 63 // 因此在设置属性时需显示的指定所在的作用域,在获取属性的时候可以指定查找的作用域。也可以选择根据作用域优先级 64 // 自动进行查找。 65 // 但是脚本执行上下文所能包含的作用域可以通过 getScopes 方法得到而不能随意指定 66 System.out.println(context.getScopes()); // [100, 200] 67 // ScriptContext预先定义了两个作用域: ENGINE_SCOPE(当前脚本引擎) 和 GLOBAL_SCOPE(从同一引擎工厂创建出来的所有脚本引擎),前者的优先级更高 68 context.setAttribute("name", "Alex", ScriptContext.GLOBAL_SCOPE); 69 context.setAttribute("name", "Bob", ScriptContext.ENGINE_SCOPE); 70 System.out.println(context.getAttribute("name")); // Bob 71 // ScriptContext的setbindings方法设置的语言绑定对象会影响到ScriptEngine在执行脚本时的变量解析。 72 // ScriptEngine的put和get方法所操作的实际上就是ScriptContext中作用域为ENGINE_SCOPE的语言绑定对象。 73 // 从ScriptContext中得到语言绑定对象之后,可以直接对这个对象进行操作。如果在ScriptEngine的eval中没有 74 // 指明所使用的语言绑定对象,实际上起作用的是ScriptContext中作用域为ENGINE_SCOPE的语言绑定对象。 75 Bindings binding1 = engine.createBindings(); 76 binding1.put("name", "Alex"); 77 context.setBindings(binding1, ScriptContext.GLOBAL_SCOPE); 78 Bindings binding2 = engine.createBindings(); 79 binding2.put("name", "Bob2"); 80 context.setBindings(binding2, ScriptContext.ENGINE_SCOPE); 81 System.out.println(engine.get("name")); // Bob2 82 Bindings binding3 = context.getBindings(ScriptContext.ENGINE_SCOPE); 83 binding3.put("name", "Alex2"); 84 System.out.println(engine.get("name")); // Alex2 85 context.setAttribute("name", "Bob3", ScriptContext.ENGINE_SCOPE); 86 System.out.println(engine.get("name")); // Bob3 87 88 // Part 4: 脚本的编译 89 // 脚本语言一般是解释执行的,脚本引擎在运行时需要先解析脚本之后再执行。一般来说 90 // 通过解释执行的方式运行脚本的速度比先编译再运行会慢一些。所以对于需要多次执行的脚本,我们 91 // 可以选择先编译,以防止重复解析。不是所有的脚本语言都支持对脚本进行编译,如果脚本支持 92 // 编译,他会实现 javax.script.Compilable接口。编译的结果用CompiledScript来表示。 93 if (engine instanceof Compilable) { 94 CompiledScript script = ((Compilable) engine).compile("var a = 12; b = a * 3;"); 95 script.eval(); 96 } 97 98 // Part 5: 方法调用 99 // 有些脚本引擎允许使用者单独调用脚本中的某个方法。支持这种调用方法的脚本引擎可以实现 100 // javax.script.Invocable 接口。通过Invocable接口既可以调用脚本中的顶层方法,也可一 101 // 调用对象中的成员方法。如果脚本中的顶层方法或者对象中的成员方法实现了java中的接口, 102 // 可以通过Invocable接口中的方法来获取及脚本中对应的java接口 的实现对象。这样就可以 103 // 在java中定义借口,在脚本中实现接口。程序中使用该接口的其他部分代码并不知道接口是 104 // 由脚本来实现的。 105 String scriptText = "function greet(name) {return 'hello ' + name; }"; 106 engine.eval(scriptText); 107 Invocable invocable = (Invocable) engine; 108 System.out.println(invocable.invokeFunction("greet", "Alex")); // hello Alex 109 // 如果调用的是脚本中对象的成员方法,则需要用invokeMethod. 110 scriptText = "var obj = {getGreeting: function(name) {return 'hello ' + name;}};"; 111 engine.eval(scriptText); 112 Object scope = engine.get("obj"); 113 System.out.println(invocable.invokeMethod(scope, "getGreeting", "Bob")); // hello Bob 114 // 在脚本中实现接口 115 scriptText = "function getGreeting(name) {return 'Hello ' + name;}"; 116 engine.eval(scriptText); 117 Greet greet = invocable.getInterface(Greet.class); // 接口必须是public类型的 118 System.out.println(greet.getGreeting("KiDe")); 119 } 120 121 private ScriptEngine getJavaScriptEngine() { 122 ScriptEngineManager manager = new ScriptEngineManager(); 123 // PS: 通过脚本引擎管理者来获取对应引擎,有三种方法:一是通过getEngineByName(这时只能是 javascript 或者 124 // JavaScript) 125 ScriptEngine engine = manager.getEngineByName("JavaScript"); 126 // 第二种方法是通过 getEngineByExtension (这时候只能是全小写的js) 127 // engine = manager.getEngineByExtension("js"); 128 // 第三种方法是通过 getEngineByMimeType (这时候也必须是全小写的 text/javascript) 129 // engine = manager.getEngineByMimeType("text/javascript"); 130 131 return engine; 132 } 133 }
1 package com.rampage.jdk7.chapter2; 2 3 public interface Greet { 4 String getGreeting(String name); 5 }