脚本(Script),是使用一种特定的描述性语言,依据一定的格式编写的可执行文件。
脚本包 Java
脚本功能是在javax.script
包中。这是一个比较小的,简单的API
。脚本的出发点是 ScriptEngineManager
类。一个ScriptEngineManager
对象可以通过jar
文件的服务发现机制发现脚本引擎。它也可以实例化脚本引擎来解释使用特定的脚本语言编写的脚本。使用脚本编程接口的最简单的方法如下:
创建一个ScriptEngineManager
对象
从ScriptEngineManager
获取 ScriptEngine
对象
使用ScriptEngine
的eval
方法执行脚本
下面给出一些例子:
举些栗子 hello world 啥也不说了,直接给俺 hello world
起来,我们用 JavaScript
来输出 hello world
吧。
1 2 3 4 5 6 7 8 public static void main (String[] args) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); engine.eval("print('hello world!')" ); }
执行如图所示:
还是可以滴!虽然它提示好像要被移除什么的,甭管这些。
执行脚本文件 执行一行代码权当我们玩玩,下面我们执行一个 js
文件看看。
怎么执行?我们调用eval
方法来接收java.io.Reader
作为输入源即可。
1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) throws FileNotFoundException, ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); File file = new File("test.js" ); FileReader fr = new FileReader(file); engine.eval(fr); }
test.js
文件中可以随便写一些 JavaScript
代码:
1 2 3 4 5 print("this is a JavaScript sccipt..." ) function f (a,b ) { return a+b } print(f(100 ,200 ))
我们执行main方法,结果如下:
脚本变量 我们在我们的Java程序中嵌入脚本,但有的时候希望脚本能够访问我们Java程序的一些对象,下面就给出了将对象作为全局变量暴露在脚本中。
我们先确保我们有一个 Person
类,它有姓名和年龄两个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ScriptVar { public static void main (String[] args) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); Person person = new Person("张三" , 20 ); engine.put("person" ,person); engine.eval("print(typeof(person))" ); engine.eval("print(person)" ); } }
上述代码就把我们创建的一个 Person
对象放入到脚本中,当然在脚本中我们也能获取它。执行结果如下:
调用脚本函数和方法 下面演示一下在Java
代码调用一个特定的脚本函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void main (String[] args) throws ScriptException, NoSuchMethodException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); String script = "function hello(name){ print('Hello, '+name+'!') }" ; engine.eval(script); Invocable inv = (Invocable) engine; inv.invokeFunction("hello" , "张三" ); }
执行结果:
脚本语言是基于对象 (如JavaScript
)或面向对象 的,你可以在脚本对象上调用脚本方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void Test1 () throws ScriptException, NoSuchMethodException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); String script = "var o = new Object();o.hello = function(name){ print('Hello, '+name+'!') }" ; engine.eval(script); Object o = engine.get("o" ); Invocable inv = (Invocable) engine; inv.invokeMethod(o,"hello" ,"李四" ); }
执行结果:
通过脚本实现Java
接口 有些时候通过脚本函数或者方法可以很方便的实现java
接口,而不是在Java
中调用。同时,通过接口我们可以避免在很多地方使用javax.script API
接口。我们可以得到一个接口实现者对象并将其传递给不同的Java api
。下面的例子演示了通过脚本实现java.lang.Runnable
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void main (String[] args) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); String script = "function run(){ print('执行了run方法...') }" ; engine.eval(script); Invocable inv = (Invocable) engine; Runnable runnable = inv.getInterface(Runnable.class ) ; Thread thread = new Thread(runnable); thread.start(); }
输出结果:
1 2 Warning: Nashorn engine is planned to be removed from a future JDK release 执行了run方法...
脚本的多作用域 在script variables
例子中,我们看到怎样将应用对象暴露为脚本的全局变量。它有可能暴露为多个全局的作用域 。 单作用域是javax.script.Bindings
的实例中. 这个借口派生至java.util.Map
。 scope
键值对的集合,其中键为非空、非空字符串。 多scopes
是 javax.script.ScriptContext
接口支持的。支持一个或多个脚本上下文与相关的域绑定。默认情况下, 每一个脚本引擎都有一个默认的脚本上下文。 默认的脚本上下文有至少一个域叫 ENGINE_SCOPE
。不同域的脚本上下文支持可以通过getscopes
方法获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); engine.put("name" ,"张三" ); engine.eval("print(name)" ); SimpleScriptContext context = new SimpleScriptContext(); Bindings engine_scope = context.getBindings(ScriptContext.ENGINE_SCOPE); engine_scope.put("name" ,"李四" ); engine.eval("print(name)" ,context); }
输出结果:
1 2 3 Warning: Nashorn engine is planned to be removed from a future JDK release 张三 李四
JavaScript与Java的通信 在大多数情况下,访问Java类
、对象
和方法
很简单。从JavaScript
中访问属性和方法与同Java
中一样。这里,我们突出JavaScript Java
访问的重要方面。下面是一些JavaScript
访问Java
的代码片段。
引入Java 包, 类 1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) throws FileNotFoundException, ScriptException { File file = new File("packge.js" ); FileReader fr = new FileReader(file); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript" ); engine.eval(fr); }
关键在于 packge.js
这个文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 Packages.java.awt; Packages.java.awt.Frame; var frame = new java.awt.Frame("hello" );frame.setVisible(true ); print(frame.title)
执行结果:
请注意,java.lang
不是默认引入的 (与Java
不同),因为会与JavaScript's
内置的 Object
, Boolean
, Math
等冲突。
importPackage
和importClass
函数”污染” 了JavaScript中的全局变量。为了避免这种情况,可以使用JavaImporter
。
实现Java 接口 在JavaScript
中,可以使用Java
匿名类语法形式实现Java
中接口:
1 2 3 4 5 6 7 8 9 var r = new java.lang.Runnable() { run: function() { print("running...\n" ); } }; var th = new java.lang.Thread(r);th.start();
当接口中只有一个需要实现的方法时,你可以自己传入脚本的函数(因为可以自动转换)。
1 2 3 4 5 6 7 function func ( ) { print("I am func!" ); } var th = new java.lang.Thread(func);th.start();