科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道动态调用动态语言之Java脚本API

动态调用动态语言之Java脚本API

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

Java SE 6 中添加的 javax.script 包使集成动态语言更加容易。通过使用一小组接口和具体类

作者:Tom McQueeney 来源:天极论坛整理 2007年10月12日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

在本页阅读全文(共3页)

 通过脚本代码调用 Java 方法

  清单 3 和 清单 4 中的示例展示了 Java 代码如何调用脚本语言中定义的函数或方法。您可能会问:脚本语言中编写的代码是否可以反过来对 Java 对象调用方法呢?答案是可以。清单 5 中的 invokeJavaFromScriptFunction() 方法显示了如何使脚本引擎能够访问 Java 对象,以及脚本代码如何才能对这些 Java 对象调用方法。明确的说,invokeJavaFromScriptFunction() 方法使用脚本引擎的 put() 方法将 HelloScriptingWorld 类的实例本身提供给引擎。当引擎拥有 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, <parameter>' 字符串。eval() 方法中的 JavaScript 代码将 Java 返回值赋给 msg 变量,然后再将其打印输出给控制台。

  Java 对象转换

  当脚本引擎使运行于引擎环境中的脚本能够使用 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) 方法为引擎设置全局绑定。

  ScriptContext 是一个定义和控制脚本引擎运行时上下文的接口。脚本引擎的 ScriptContext 含有 “引擎” 和 “全局” 作用域绑定,以及用于标准输入和输出操作的输入和输出流。您可以使用引擎的 getContext() 方法获取并操作脚本引擎的上下文。

  一些脚本 API 概念,比如说作用域、绑定 和上下文,开始看来会令人迷惑,因为它们的含义有交叉的地方。本文的源代码下载文件含有一个名为 ScriptApiRhinoTest 的 JUnit 测试文件,位于 src/test/java directory 目录,该文件可以通过 Java 代码帮助解释这些概念。

  未来的计划

  现在,大家已经对 Java 脚本 API 有了最基本的认识,本系列文章的第 2 部分将在此基础上进行扩展,为大家演示一个更为实际的示例应用程序。该应用程序将使用 Groovy、Ruby 和 JavaScript 一起编写的外部脚本文件来定义可在运行时修改的业务逻辑。如您如见,在脚本语言中定义业务规则可以使规则的编写更加轻松,并且更易于程序员之外的人员阅读,比如说业务分析师或规则编写人员。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章