快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

bet356亚洲版体育在线_酒文化网进入



我们不必要将动态说话编译为 Java字节码就可以在 Java 利用法度榜样中应用它们。应用 Java Platform, Standard Edition 6 (Java SE)中添加的脚本包(并且向后兼容 Java SE 5),Java 代码可以在运行时以一种简单的、统一的要领调用多种动态说话。本系列文章共分两个部分,第 1 部分将先容 Java 脚本 API 的各类特点。文章将应用一个简单的 Hello World 利用法度榜样展示 Java 代码若何履行脚本代码以及脚本若何反过来履行 Java 代码。第 2 部分将深入钻研 Java 脚本 API 的强大年夜功能。

Java 开拓职员清楚 Java 并不是在任何环境下都是最佳的说话。今年,1.0 版本的 JRuby 和 Groovy 的发行引领了一场热潮,匆匆使人们纷繁在自己的 Java 利用法度榜样中添加动态说话。Groovy、JRuby、Rhino、Jython 和一些其他的开源项目使在所谓的脚本说话中编写代码并在 JVM 中运行成为了可能(请参阅 参考资料)。平日,在 Java 代码中集成这些说话必要对各类说冥器所特有的 API 和特点有所懂得。

Java SE 6 中添加的 javax.script 包使集成动态说话加倍轻易。经由过程应用一小组接口和详细类,这个包使我们能够简单地调用多种脚本说话。然则,Java 脚本 API 的功能不光是在利用法度榜样中编写脚本;这个脚本包使我们能够在运行时读取和调用外部脚本,这意味着我们可以动态地改动这些脚本从而变动运行利用法度榜样的行径。

Java 脚本 API

脚本与动态的比较

术语脚本 平日表示在说冥器 shell 中运行的说话,它们每每没有零丁的编译步骤。术语动态 平日表示等到运行时判断变量类型或工具行径的说话,每每具有闭包和继续特点。一些通用的编程说话同时具有这两种特点。此处首选脚本说话 是由于本文的着重点是 Java 脚本 API,而不是由于说起的说话缺少动态特点。

2006 年 10 月,Java 说话添加了脚本包,从而供给了一种统一的要领将脚本说话集成到 Java 利用法度榜样中去。对付说话开拓职员,他们可以应用这个包编写粘连代码(glue code),从而使人们能够在 Java 利用法度榜样中调用他们的说话。对付 Java 开拓职员,脚本包供给了一组类和接口,容许应用一个公共 API 调用多种说话编写的脚本。是以,脚本包类似于不合说话(比如说不合的数据库)中的 Java Database Connectivity (JDBC) 包,可以应用同等的接口集成到 Java 平台中去。

曩昔,在 Java 代码中,动态调用脚本说话涉及到应用各类说话发行版所供给的独特类或应用 Apache 的 Jakarta Bean Scripting Framework (BSF)。BSF 在一个 API 内部统一了一组脚本说话(请参阅 参考资料)。应用 Java SE 6 脚本 API,二十余种脚本说话(AppleScript、Groovy、JavaScript、Jelly、PHP、Python、Ruby 和 Velocity)都可以集成到 Java 代码中,这在很大年夜法度榜样上依附的是 BSF。

脚本 API 在 Java 利用法度榜样和外部脚本之间供给了双向可见性。Java 代码不仅可以调用外部脚本,而且还容许那些脚本造访选定的 Java 工具。比如说,外部 Ruby 脚本可以对 Java 工具调用措施,并造访工具的属性,从而使脚本能够将行径添加到运行中的利用法度榜样中(假如在开拓时无法估计利用法度榜样的行径)。

调用外部脚本可用于运行时利用法度榜样增强、设置设置设备摆设摆设、监控或一些其他的运行时操作,比如说在不绝止利用法度榜样的环境下改动营业规则bet356亚洲版体育在线。脚本包可能的感化包括:

在比 Java 说话更简单的说话中编写营业规则,而不用借助成熟的规则引擎。

创建插件架构,应用户能够动态地定制利用法度榜样。

将已有脚本集成到 Java 利用法度榜样中,比如说处置惩罚或转换文件文章的脚本。

应用成熟的编程说话(而不是属性文件)从外部设置设置设备摆设摆设利用法度榜样的运行时行径。

在 Java 利用法度榜样中添加一门特定于域的说话(domain-specific language)。

在开拓 Java 利用法度榜样原型的历程中应用脚本说话。

在脚本说话中编写利用法度榜样测试代码。

你好,脚本天下

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 组件关系

您可能会问:若何才能获取脚本引擎实现和说话说冥器所需的 JAR 文件呢?最好的措施是在 java.net 上托管的开源 Scripting 项目中查找脚本引擎实现(请参阅 参考资料)。您可以在 java.net 上找到许多说话的脚本引擎实现和其他网站的链接。Scripting 项目还供给了各类链接,经由过程这些链接可以下载受支持的脚本说话的说冥器。

在 清单 1 中,main() 措施将 ScriptEngine 通报给各个措施用于谋略该措施的 JavaScript 代码。第一个措施如清单 2 所示。invokeHelloScript() 措施调用脚本引擎的 eval 措施谋略和履行 JavaScript 代码中的特定字符串。ScriptEngine 接口定义了 6 个重载的 eval() 措施,用于将接管的脚本算作字符串或 java.io.Reader 工具谋略,java.io.Reader 工具一样平常用于从外部源(例如文件)读取脚本。

清单 2. invokeHelloScript 措施

private static void invokeHelloScript(ScriptEngine jsEngine) throws ScriptException {

jsEngine.eval("println('Hello from JavaScript')");

}

脚本履行高低文

HelloScriptingWorld 利用法度榜样中的示例脚本 应用 JavaScript println() 函数向节制台输出结果,然则我们拥有输入和输出流的完全节制权。脚本引擎供给了一个选项用于改动脚本履行的高低文,这意味着我们可以改动标准输入流、标准输出流和标准差错流,同时还可以定义哪些全局变量和 Java 工具对正在履行的脚本可用。

invokeHelloScript() 措施中的 JavaScript 将 Hello from JavaScript 输出到标准输出流,在本例中为节制台窗口。(清单 6 含有运行 HelloScriptingWorldApplication 时的完备输出。)

留意,类中的这一措施和其他措施都声明抛出了 javax.script.ScriptException。这个选中的非常(脚本包中定义的惟逐一个非常)表示引擎无法解析或履行给定的代码。所有脚本引擎 eval() 措施都声明抛出一个 ScriptException,是以我们的代码必要适当处置惩罚这些非常。

清单 3 显示了两个有关的措施:defineScriptFunction() 和 invokeScriptFunctionFromEngine()。defineScriptFunction() 措施还应用一段硬编码的 JavaScript 代码调用脚本引擎的 eval() 措施。然则有一点必要留意,该措施的所有事情只是定义了一个 JavaScript 函数 sayHello()。并没有履行任何代码。sayHello() 函数只有一个参数,它会应用 println() 语句将这个参数输出到节制台。脚本引擎的 JavaScript 说冥器将这个函数添加到全局情况,以供后续的 eval 调用应用(该调用发生在 invokeScriptFunctionFromEngine() 措施中,这并不稀罕)。

清单 3. defineScriptFunction 和 invokeScriptFunctionFromEngine 措施

private static void defineScriptFunction(ScriptEngine engine) throws ScriptException {

// Define a function in the script engine

engine.eval(

"function sayHello(name) {" +

" println('Hello, ' + name)" +

"}"

);

}

private static void invokeScriptFunctionFromEngine(ScriptEngine engine)

throws ScriptException

{

engine.eval("sayHello('World!')");

}

这两个措施演示了脚本引擎可以保持利用法度榜样组件的状态,并且能够在后续的 eval() 措施调用历程中应用其状态。invokeScriptFunctionFromEngine() 措施可以使用所保持的状态,措施是调用定义在 eval() 调用中的 sayHello() JavaScript 函数。

许多脚本引擎在 eval() 调用之间保bet356亚洲版体育在线持全局变量和函数的状态。然则有一点值得非分特别留意,Java 脚本 API 并不要求脚本引擎供给这一特点。本文中所应用的 JavaScript、Groovy 和 JRuby 脚本引擎确凿在 eval() 调用之间保持了这些状态。

清单 4 中的代码在前一个示例的根基上做了几分改动。原本的 invokeScriptFunctionFromJava() 措施在调用 sayHello() JavaScript 函数时没有应用 ScriptEngine 的 eval() 措施或 JavaScript 代码。与此不合,清单 4 中的措施应用 Java 脚本 API 的 javax.script.Invocable 接口调用由脚本引擎所保持的函数。invokeScriptFunctionFromJava() 措施将脚本引擎工具通报给 Invocable 接口,然后对该接口调用 invokeFunction() 措施,终极应用给定的参数调用 sayHello() JavaScript 函数。假如调用的函数必要返回值,则 invokeFunction() 措施会将值封装为 Java 工具类型并返回。

清单 4. invokeScriptFunctionFromJava 措施

private static void invokeScriptFunctionFromJava(ScriptEngine engine)

throws ScriptException, NoSuchMethodException

{

Invocable invocableEngine = (Invocable) engine;

invocableEngine.invokeFunction("sayHello", "from Java");

}

应用代理实现高档脚本调用

当脚本函数或措施实现了一个 Java 接口时,就可以应用高档 Invocable。Invocable 接口定义了一个 getInterface() 措施,该措施应用接口做为参数并且将返回一个实现该接口的 Java 代码工具。从脚本引擎得到代理工具之后,可以将它作为正常的 Java 工具对待。对该代理调用的措施将委托给脚本引擎经由过程脚本说话履行。

留意,清单 4 中没有 JavaScript 代码。Invocable 接口容许 Java 代码调用脚本函数,而无需知道着实现说话。假如脚本引擎无法找到给定名称或参数类型的函数,那么 invokeFunction() 措施将抛出一个 java.lang.NoSuchMethodException。

Java 脚本 API 并不要求脚本引擎实现 Invocable 接口。实际上,清单 4 中的代码应该应用 instanceof 运算符确保脚本引擎在转换(cast)之前实现了 Invocable 接口。

经由过程脚本代码调用 Java 措施

清单 3 和 清单 4 中的示例展示了 Java 代码若何调用脚本说话中定义的函数或措施。您可能会问:脚本说话中编写的代码是否可以反过来对 Java 工具调用措施呢?谜底是可以。清单 5 中的 invokeJavaFromScriptFunction() 措施显示了若何使脚本引擎能够造访 Java 工具,以及脚本代码若何才能对这些 Java 工具调用措施。明确的说,invokeJavaFromScriptFunction() 措施应用脚本引擎的 put() 措施将 HelloScribet356亚洲版体育在线ptingWorld 类的实例本身供给给引擎。当引擎拥有 Java 工具的造访权之后(应用 put() 调用所供给的名称),eval() 措施脚本中的脚本代码将应用该工具。

清单 5. invokeJavaFromScriptFunction 和 getHelloReply 措施

private static void invokeJavaFromScriptFunction(ScriptEngine engine)

throws ScriptException

{

engine.put("helloScriptingWorld", new HelloScriptingWorld());

engine.eval(

"println('Invoking getHelloReply method from JavaScript...');" +

"var msg = helloScriptingWorld.getHelloReply(vJavaScript');" +

"println('Java returned: ' + msg)"

);

}

/** Method invoked from the above script to return a string. */

public String getHelloReply(String name) {

return "Java method getHelloReply says, 'Hello, " + name + "'";

}

清单 5 中的 eval() 措施调用中所包孕的 JavaScript 代码应用脚本引擎的 put() 措施调用所供给的变量名称 helloScriptingWorld 造访并应用 HelloScriptingWorld Java 工具。清单 5 中的第二行 JavaScript 代码将调用 getHelloReply() 公有 Java 措施。getHelloReply() 措施将返回 Java method getHelloReply says, 'Hello,

' 字符串。eval() 措施中的 JavaScript 代码将 Java 返回值赋给 msg 变量,然后再将其打印输出给节制台。

Java 工具转换

当脚本引擎使运行于引擎bet356亚洲版体育在线情况中的脚本能够应用 Java 工具时,引擎必要将其封装到适用于该脚本说话的工具类型中。封装可能会涉及到一些适当的工具-值转换,比如说容许 Java Integer 工具直接在脚本说话的数学表达式中应用。关于若何将 Java 工具转换为脚本工具的钻研是与各个脚本说话的引擎分皮毛关的,并且不在本文的评论争论范围之内。然则,您应该意识到转换的发生,由于可以经由过程测试来确保所应用的脚本说话履行转换的要领相符您的期望。

ScriptEngine.put 及其相关 get() 措施是在运行于脚本引擎中的 Java 代码和脚本之间共享工具和数据的主要道路。(有关这一方面的具体叙述,请参阅本文后面的 Script-execution scope 一节。)当我们调用引擎的 put() 措施时,脚本引擎会将第二个参数(任何 Java 工具)关联到特定的字符串关键字。大年夜多半脚本引擎都是让脚本应用特定的变量名称来造访 Java 工具。脚本引擎可以随意对待通报给 put() 措施的名称。比如说,JRuby 脚本引擎让 Ruby 代码应用全局 $helloScriptingWorld 工具造访 helloScriptingWorld,以相符 Ruby 全局变量的语法。

脚本引擎的 get() 措施检索脚本情况中可用的值。一样平常而言,Java 代码经由过程 get() 措施可以造访脚本情况中的所有全局变量和函数。然则只有明确应用 put() 与脚本共享的 Java 工具才可以被脚本造访。

外部脚本在运行着的利用法度榜样中造访和操作 Java 工具的这种功能是扩展 Java 法度榜样功能的一项强有力的技术。(第 2 部分将经由过程示例钻研这一技术)。

运行 HelloScriptingWorld 利用法度榜样

您可以经由过程下载和构建源代码来运行 HelloScriptingWorld 利用法度榜样。此 .zip 中文件含有一个 Ant 脚本和一个 Maven 构建脚本,可以赞助大年夜家编译和运行示例利用法度榜样。请履行以下步骤:

下载 此 .zip 文件。

创建一个新目录,比如说 java-scripting,并将步骤 1 中所下载的文件解压到该目录中。

打开敕令行 shell 并转到该目录。

运行 ant run-hello 敕令。

您应该可以看到类似于清单 6 的 Ant 节制台输出。留意,defineScriptFunction() 函数没有孕育发生任何输出,由于它虽然定义了输出然则却没有调用 JavaScript 函数。

清单 6. 运行 HelloScriptingWorld 时的输出

Calling invokeHelloScript...

Hello from JavaScript

Calling defineScriptFunction...

Calling invokeScriptFunctionFromEngine...

Hello, World!

Calling invokeScriptFunctionFromJava...

Hello, from Java

Calling invokeJavaFromScriptFunction...

Invoking getHelloReply method from JavaScript...

Java returned: Java method getHelloReply says, 'Hello, JavaScript'

Java 5 兼容性

Java SE 6 引入了 Java 脚本 API,然则您也可以应用 Java SE 5 运行此 API。只必要供给缺少的 javax.script 包类的一个实现即可。所幸的是,Java Specification Request 223 参考实现中含有这个实现(请参阅 参考资料 得到下载链接。)JSR 223 对 Java 脚本 API 做出了定义。

假如您已经下载了 JSR 223 参考实现,解压下载文件并将 script-api.jar、script-js.jar 和 js.jar 文件复制到您的类路径下。这些文件将供给脚本 API、JavaScript 脚本引擎接口和 Java SE 6 中所附带的 JavaScript 脚本引擎。

脚本履行感化域

与简单地调用引擎的 get() 和 put() 措施比拟,若何将 Java 工具公开给运行于脚本引擎中的脚本具有更好的可设置设置设备摆设摆设性。当我们在脚本引擎上调用 get() 或 put() 措施时,引擎将会在 javax.script.Bindings 接口的默认实例中检索或保存所哀求的关键字。(Bindings 接口只是一个 Map 接口,用于强制关键字为字符串。)

现代码调用脚本引擎的 eval() 措施时,将应用引擎默认绑定的关键字和值。然则,您可以为 eval() 调用供给自己的 Bindings 工具,以限定哪些变量和工具对付该特定脚本可见。该调用外表上类似于 eval(String, Bindings) 或 eval(Reader, Bindings)。要赞助您创建自定义的 Bindings,脚本引擎将供给一个 createBindings() 措施,该措施和返回值是一个内容为空的 Bindings 工具。应用 Bindings 工具临时调用 eval 将暗藏先前保存在引擎默认绑定中的 Java 工具。

要添加功能,脚本引擎含有两个默认绑定:其一为 get() 和 put() 调用所应用的 “引擎感化域” 绑定 ;其二为 “全局感化域” 绑定,当无法在 “引擎感化域” 中找到工具时,引擎将应用第二种绑定进行查找。脚本引擎并不必要使脚本能够造访全局绑定。大年夜多半脚本都可以造访它。

“全局感化域” 绑定的设计目的是在不合的脚本引擎之间共享工具。ScriptEngineManager 实例返回的所有脚本引擎都是 “全局感化域” 绑定工具。您可以应用 getBindings(ScriptContext.GLOBAL_SCOPE) 措施检索某个引擎的全局绑定,并且可以应用 setBindings(Bindings, ScriptContext.GLOBAL_SCOPE) 措施为引擎设置全局绑定。

ScriptContebet356亚洲版体育在线xt 是一个定义和节制脚本引擎运行时高低文的接口。脚本引擎的 ScriptContext 含有 “引擎” 和 “全局” 感化域绑定,以及用于标准输入和输出操作的输入和输出流。您可以应用引擎的 getContext() 措施获取并操作脚本引擎的高低文。

一些脚本 API 观点,比如说感化域、绑定 和高低文,开始看来会令人迷惑,由于它们的含义有交叉的地方。本文的源代码下载文件含有一个名为 ScriptApiRhinoTest 的 JUnit 测试文件,位于 src/test/java directory 目录,该文件可以经由过程 Java 代码赞助解释这些观点。

未来的计划

现在,大年夜家已经对 Java 脚本 API 有了最基础的熟识,本系列文章的第 2 部分将在此根基长进行扩展,为大年夜家演示一个更为实际的示例利用法度榜样。该利用法度榜样将应用 Groovy、Ruby 和 JavaScript 一路编写的外部脚本文件来定义可在运行时改动的营业逻辑。如您如见,在脚本说话中定义营业规则可以使规则的编写加倍轻松,并且更易于法度榜样员之外的职员涉猎,比如说营业阐发师或规则编写职员。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: