你好,脚本世界
HelloScriptingWorld 类(本文中的相关代码均可从 下载部分 获得)演示了 Java 脚本包的一些关键特性。它使用硬编码的 JavaScript 作为示例脚本语言。此类的 main() 方法(如清单 1 所示)将创建一个 JavaScript 脚本引擎,然后分别调用五个方法(在下文的清单中有显示)用于突出显示脚本包的特性。
清单 1. HelloScriptingWorld main 方法
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager scriptEngineMgr = new ScriptEngineManager();
ScriptEngine jsEngine = scriptEngineMgr.getEngineByName("JavaScript");
if (jsEngine == null) {
System.err.println("No script engine found for JavaScript");
System.exit(1);
}
System.out.println("Calling invokeHelloScript...");
invokeHelloScript(jsEngine);
System.out.println("\nCalling defineScriptFunction...");
defineScriptFunction(jsEngine);
System.out.println("\nCalling invokeScriptFunctionFromEngine...");
invokeScriptFunctionFromEngine(jsEngine);
System.out.println("\nCalling invokeScriptFunctionFromJava...");
invokeScriptFunctionFromJava(jsEngine);
System.out.println("\nCalling invokeJavaFromScriptFunction...");
invokeJavaFromScriptFunction(jsEngine);
}
main() 方法的主要功能是获取一个 javax.script.ScriptEngine 实例(清单 1 中的前两行代码)。脚本引擎可以在特定的语言中加载并执行脚本。它是 Java 脚本包中使用最为频繁、作用最为重要的类。我们从 javax.script.ScriptEngineManager 获取一个脚本引擎(第一行代码)。通常,程序只需要获取一个脚本引擎实例,除非使用了很多种脚本语言。
ScriptEngineManager 类
ScriptEngineManager 可能是脚本包中惟一一个经常使用的具体类;其他大多数都是接口。它或许是脚本包中惟一的一个要直接或间接地(通过 Spring Framework 之类的依赖性注入机制)实例化的类。ScriptEngineManager 可以使用以下三种方式返回脚本引擎:
·通过引擎或语言的名称,比如说 清单 1 请求 JavaScript 引擎。
·通过该语言脚本共同使用的文件扩展名,比如说 Ruby 脚本的 .rb。
·通过脚本引擎声明的、知道如何处理的 MIME 类型。
本文示例为什么要使用 JavaScript?
本文中的 Hello World 示例使用了部分 JavaScript 脚本,这是因为 JavaScript 代码易于理解,不过主要还是因为 Sun Microsystems 和 BEA Systems 所提供的 Java 6 运行时环境附带有基于 Mozilla Rhino 开源 JavaScript 实现的 JavaScript 解释器。使用 JavaScript,我们无需在类路径中添加脚本语言 JAR 文件。
ScriptEngineManager 间接查找和创建脚本引擎。也就是说,当实例化脚本引擎管理程序时,ScriptEngineManager 会使用 Java 6 中新增的服务发现机制在类路径中查找所有注册的 javax.script.ScriptEngineFactory 实现。这些工厂类封装在 Java 脚本 API 实现中;也许您永远都不需要直接处理这些工厂类。
ScriptEngineManager 找到所有的脚本引擎工厂类之后,它会查询各个类并判断是否能够创建所请求类型的脚本引擎 —— 清单 1 中为 JavaScript 引擎。如果工厂说可以创建所需语言的脚本引擎,那么管理程序将要求工厂创建一个引擎并将其返回给调用者。如果没有找到所请求语言的工厂,那么管理程序将返回 null,清单 1 中的代码将检查 null 返回值并做出预防。
ScriptEngine 接口
如前所述,代码将使用 ScriptEngine 实例执行脚本。脚本引擎充当脚本代码和最后执行代码的底层语言解释器或编译器之间的中间程序。这样,我们就不需要了解各个解释器使用哪些类来执行脚本。比如说,JRuby 脚本引擎可以将代码传递给 JRuby 的 org.jruby.Ruby 类的一个实例,首先将脚本编译成中间形式,然后再调用它计算脚本并处理返回值。脚本引擎实现隐藏了一些细节,包括解释器如何与 Java 代码共享类定义、应用程序对象和输入/输出流。
图 1 显示了应用程序、Java 脚本 API 和 ScriptEngine 实现、脚本语言解释器之间的总体关系。我们可以看到,应用程序只依赖于脚本 API,它提供了 ScriptEngineManager 类和 ScriptEngine 接口。ScriptEngine 实现组件处理使用特定脚本语言解释器的细节。

图 1:脚本 API 组件关系