Nashorn jdk8新增的ScriptEngine官方文档翻译及代码示例

来源:互联网 发布:手机上怎么找淘宝链接 编辑:程序博客网 时间:2024/06/05 19:21

Nashorn js engine官方文档
https://docs.oracle.com/javase/7/docs/technotes/guides/scripting/programmer_guide/#top
这个js API主要面向什么人的?

脚本语言的一些有用的特性如下:

● Convenience 方便:多数脚本语言都是动态类型的。通常你可以不需要声明变量类型来创建一个对象,你也可以重复使用一个变量并给它赋予不同的对象类型。虽然这样,脚本语言还是倾向于自动执行类型转换,例如:根据需要,可以将一个数字10转换为文本“10”。
● Developing rapid prototypes 开发快速原型: 你可以避免 编辑-编译-运行 的循环,而仅仅使用 编辑-运行!
● Application extension/customization 应用程序扩展/定制:您可以“外部化”部分应用程序,例如配置脚本,金融应用程序的业务逻辑/规则和数学表达式。
● “Command line” shells for applications 为应用程序提供命令行shell:用于调试,运行时/部署时间配置等。大多数应用程序现在都有基于Web的GUI配置工具。 但是系统管理员/部署人员经常喜欢命令行工具。为了避免为此目的发明特殊脚本语言,可以使用“标准”脚本语言。
Java™ Script API是使用Java代码中的脚本引擎的脚本语言独立框架。 使用Java Scripting API,可以使用Java语言编写可定制/可扩展的应用程序,并将自定义脚本语言选择权留给用户。 Java开发人员不需要在开发过程中选择扩展语言。 如果您使用JSR-223 API编写应用程序,则您的用户可以使用人和符合JSR-223标准的脚本语言。

Scripting Package

Java脚本功能的api相关的类位于javax.script包中。这是一个相对较小,又简单的API。script API的起点/入口是ScriptEngineManager类。一个ScriptEngineManager对象可以通过jar文件服务发现机制来发现脚本引擎。它还可以实例化ScriptEngine对象,以解释以特定脚本语言编写的脚本。 使用脚本API的最简单的方法如下:
1.创建一个ScriptEngineManager对象
2.从ScriptEngineManager对象中获取ScriptEngine对象
3.利用ScriptEngine的eval方法执行脚本
现在,是时候看一些代码示例了。虽然这不是强制性的,但可能需要了解一些JavaScript来阅读这些示例。

Examples

“hello world”
通过ScriptEngineManager 示例,我们使用getEngineByName方法来请求获取一个JavaScript 引擎实例。脚本引擎的eval方法被调用后,它会执行一个给定的js代码字符串。了简洁起见,在这个以及随后的例子中,我们没有显示异常处理。javax.script API会抛出checked exceptions 和 runtime exceptions。毋庸置疑,你需要适当地处理这些异常。

import javax.script.*;public class EvalScript {    public static void main(String[] args) throws Exception {        // create a script engine manager        ScriptEngineManager factory = new ScriptEngineManager();        // create a JavaScript engine        ScriptEngine engine = factory.getEngineByName("JavaScript");        // evaluate JavaScript code from String        engine.eval("print('Hello, World')");    }}

Evaluating a Script File 读取并执行脚本文件xx.js

在这个例子中,我们调用eval方法来接受java.io.Reader 对象参数来读入一个源文件。Reader会读取脚本然后脚本被执行。这样就可以通过将相关输入流对象作为读取器包装和执行来自文件,URL和资源的脚本。

import javax.script.*;public class EvalFile {    public static void main(String[] args) throws Exception {        // create a script engine manager        ScriptEngineManager factory = new ScriptEngineManager();        // create JavaScript engine        ScriptEngine engine = factory.getEngineByName("JavaScript");        // evaluate JavaScript code from given file - specified by first argument        engine.eval(new java.io.FileReader(args[0]));    }}

Let us assume that we have the file named “test.js” with the following text:
println(“This is hello from test.js”);

We can run the above Java as
java EvalFile test.js

Script Variables 脚本变量

当您使用Java应用程序嵌入脚本引擎和脚本时,,您可能希望将应用程序对象做为脚本的全局变量。此示例演示如何将应用程序对象公开为脚本的全局变量。我们在应用中创建了一个java.io.File对象,使用名称“file”显示与全局变量相同的内容。这个脚本可以读取这个变量。例如:它可以调用这个变量的权限修饰为public的方法。请注意,访问Java对象,方法和字段的语法取决于脚本语言。 JavaScript支持最“自然”的类似Java的语法。

public class ScriptVars {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("Nashorn");        File f = new File("test.txt");        //将File对象f直接注入到js脚本中并可以作为全局变量使用        engine.put("file", f);        // evaluate a script string. The script accesses "file"        // variable and calls method on it        engine.eval("print(file.getAbsolutePath())");    }}

Invoking Script Functions and Methods java中执行js函数

有时你需要重复调用一个特定的脚本函数,例如,您的应用程序菜单功能可能由脚本实现。在你的菜单的事件处理器中你可能想调用一个脚本的函数。以下示例演示了从Java代码调用特定的脚本函数。

import javax.script.*;public class InvokeScriptFunction {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("Nashorn");        // String定义一个js函数        String script = "function hello(name) { print('Hello, ' + name); }";        // 直接执行上面的js函数(这个函数是全局的,在下面的js引擎中依然可以调用,不会执行完就消失)        engine.eval(script);        // javax.script.Invocable 是一个可选的接口        // 检查脚本引擎是否被实现!        // 注意:JavaScript engine 实现了 Invocable 接口        Invocable inv = (Invocable) engine;        // 执行这个名字为 "hello"的全局的函数        inv.invokeFunction("hello", "Scripting!!" );    }}

如果您的脚本语言是基于对象的(如JavaScript)或面向对象的,则你可以在脚本对象上调用脚本方法。

import javax.script.*;public class InvokeScriptMethod {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("JavaScript");        // 用String定义了一段JavaScript代码这段代码定义了一个对象'obj'        // 给对象增加了一个名为 hello 的方法(hello这个方法是属于对象的)        String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";        //执行这段script脚本        engine.eval(script);        // javax.script.Invocable 是一个可选的接口        // 检查你的script engine 接口是否已实现!        // 注意:JavaScript engine实现了Invocable接口        Invocable inv = (Invocable) engine;        // 获取我们想调用那个方法所属的js对象        Object obj = engine.get("obj");        // 执行obj对象的名为hello的方法        inv.invokeMethod(obj, "hello", "Script Method !!" );    }}

Implementing Java Interfaces by Scripts 使用Script实现java接口

与java直接调用特定的script函数不同,有时候使用Script的函数来实现一个java接口往往也很方便。另外,通过使用接口,我们可以避免在许多地方使用javax.script API。我们可以获得一个接口实现器对象并将其传递给各种Java API。下面的例子演示了如何在Script中实现java.lang.Runnable接口。

import javax.script.*;public class RunnableImpl {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("JavaScript");        // String里定义一段JavaScript代码脚本        String script = "function run() { println('run called'); }";        // 执行这个脚本        engine.eval(script);        Invocable inv = (Invocable) engine;        // 从脚本引擎中获取Runnable接口对象(实例). 该接口方法由具有相匹配名称的脚本函数实现。// 在上面的脚本中,我们已经实现了Runnable接口的run()方法        Runnable r = inv.getInterface(Runnable.class);        // 启动一个线程运行上面的实现了runnable接口的script脚本        Thread th = new Thread(r);        th.start();//打印输出:run called    }}

如果您的脚本语言是基于对象或面向对象的,则可以通过脚本对象的脚本方法实现Java接口。 这避免了为接口方法调用脚本全局函数。 脚本对象可以存储与接口实现者相关联的“状态”。

import javax.script.*;public class RunnableImplObject {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("JavaScript");        // String里定义一段JavaScript代码脚本        String script = "var obj = new Object(); obj.run = function() { println('run method called'); }";        // 执行这个脚本        engine.eval(script);        // 获取这个我们要用来实现接口的js对象        Object obj = engine.get("obj");        Invocable inv = (Invocable) engine;        // 通过脚本引擎获取Runnable接口对象 该接口的方法已被js对象实现        Runnable r = inv.getInterface(obj, Runnable.class);        // 启动一个线程,运行已被脚本实现的Runnable接口        Thread th = new Thread(r);        th.start();    }}

Multiple Scopes for Scripts 脚本引擎的多个scope

在上面的脚本变量(script variables)章节,我们看到如何将java对象作为Script的全局变量。可以为脚本公开/暴露多个全局“作用域”。单个scope是javax.script.Bindings的一个实例,此接口派生自java.util.Map(String,Object)。scope是一组 名称- 值对,其中name可以是任何非空字符串。javax.script.ScriptContext接口支持多个scope。Script context支持一个或多个具有每个作用域的绑定的作用域。默认情况下,每个脚本引擎都有一个默认脚本上下文。默认脚本上下文至少有一个称为“ENGINE_SCOPE”的范围。通过getScopes方法可以获得脚本上下文支持的各种范围(scope)。

import javax.script.*;public class MultiScopes {    public static void main(String[] args) throws Exception {        ScriptEngineManager manager = new ScriptEngineManager();        ScriptEngine engine = manager.getEngineByName("JavaScript");        engine.put("x", "hello");        // 打印全局变量 "x"        engine.eval("println(x);");        // 上面的代码会打印"hello"        // 现在,传入另一个不同的script context        ScriptContext newContext = new SimpleScriptContext();//新的Script context绑定ScriptContext的ScriptContext        Bindings engineScope = newContext.getBindings(ScriptContext.ScriptContext);        // 增加一个新变脸到新的范围 engineScope 中        engineScope.put("x", "world");        // 执行同一个脚本 - 但这次传入一个不同的script context        engine.eval("println(x);", newContext);        // 上面的代码会打印 "world"    }}

JavaScript Script Engine

Oracle的JDK 7 实现与基于Mozilla Rhino(https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/Rhino)的JavaScript引擎共同捆绑,可以与javax.script(JSR-223)API结合使用。
此目录和子目录包含具有Oracle更改的Rhino源。Mozilla Rhino源代码是根据Mozilla公共许可证1.1版授权的。您可以通过http://www.mozilla.org/MPL获得本许可证的副本。请注意,com.sun.script包和子包中的源不在MPL下。

Feature changes in JavaScript implementation jsvascript实现的特性变动
Rhino的大部分实现都包含在内,但Rhino的几个组件不包括/更改,主要是由于footprint和安全原因。这些特性为:
1、JavaScript-to-bytecode 即将JavaScript编译为字节码(又称为优化器):当安全管理器存在时,优化器被禁用。当不使用安全管理器时,System属性“rhino.opt.level”的值可以定义的范围为[-1,9]。默认情况下,该值设置为-1,这意味着优化器被禁用。
2、Rhino的JavaAdapter已被重写。JavaAdapter是java类可以被JavaScript扩展和Java接口可以由JavaScript实现的特性。我们已经用我们自己的JavaAdapter实现替代了Rhino的JavaAdapter。在我们的实现中,只有单个Java接口可以由JavaScript对象来实现。 例如,正如下面的代码中所预期的:

var v = new java.lang.Runnable() {                    run: function() { print('hello'); }               }       v.run();

在大多数情况下,JavaAdapter用于实现具有Java匿名器类类语法的单一接口。使用JavaAdapter来集成Java类或实现多个接口的情况非常少见。
3、Rhino命令行工具(Rhino shell,debugger等)都不包括在内。

JavaScript to Java Communication JavaScript到java的通信

在大多数情况下,访问Java类,对象和方法很简单,访问JavaScript的字段和方法访问与Java相同。我们强调JavaScript Java访问的重要方面。详情请参考http://www.mozilla.org/rhino/scriptjava.html 下面的示例是JavaScript访问Java的代码片段。本节需要JavaScript的知识。如果您打算使用其他JSR-223脚本语言而不是JavaScript,则可以跳过此部分。

Importing Java Packages, Classes 在js中引入java包和类

内置函数importPackage和importClass可用于导入Java包和类。

// 引入java的包和类// 类似于在java中引入包的写法: import package.*;importPackage(java.awt);// 类似于在java中引入类的写法:import java.awt.FrameimportClass(java.awt.Frame);// 使用:"new ClassName",在js中创建一个java对象var frame = new java.awt.Frame("hello");// 在js中调用java对象的公共的方法(private的不可调用)frame.setVisible(true);// 访问javabean对象的属性,不需要调用getter方法(直接用student.name,而不需要使用student.getName())print(frame.title);

Packages这个全局变量可用于访问Java包。例如: Packages.java.util.Vector, Packages.javax.swing.JFrame. 请注意:java是 “Packages.java”的缩写。 javax,org,edu,com,net prefix等jdk包都有等效的简写方式,所以几乎所有的JDK平台类都可以被访问而不需要写“Packages”前缀。
请注意,与Java不同,java.lang不会被默认导入,因为这将导致与JavaScript内置的Object,Boolean,Math等相冲突。
importPackage和importClass函数可能会“污染”JavaScript的全局变量。 为避免这种情况,您可以使用JavaImporter。

// 创建具有要导入的特定包和类的JavaImportervar SwingGui = new JavaImporter(javax.swing,                            javax.swing.event,                            javax.swing.border,                            java.awt.event);with (SwingGui) {    // 在这个'with'语句中,我们可以通过不合格(简单的,直接调用类名)的名字访问Swing和AWT类。    var mybutton = new JButton("test");    var myframe = new JFrame("test");}

Creating and Using Java Arrays 创建和使用java数组

在js中创建java对象与在java中创建对象的方式是相同的,但在js中创建java数组时,我们需要明确地使用java反射。但是一旦数组被创建,那么数组的元素访问和length属性的访问都与java中相同。此外,当Java方法期望Java数组时,可以使用脚本数组(程序会自动转换)。

// 创建长度为5的java数组var a = java.lang.reflect.Array.newInstance(java.lang.String, 5);// 在js中使用Java语法访问数组的元素和长度a[0] = "scripting is great!";print(a.length);

Implementing Java Interfaces 实现java接口

在js中,可以使用类似java匿名内部类的语法来实现一个java接口。

var r  = new java.lang.Runnable() {    run: function() {        print("running...\n");    }};// 可以将“r”传递给接收java.lang.Runnable参数的Java方法var th = new java.lang.Thread(r);th.start();

When an interface with a single method is expected, you can pass a script function directly.(auto conversion)
当接口需要接收一个简单的方法时,可以直接传递js脚本函数:

function func() {     print("I am func!");}// pass script function for java.lang.Runnable argument//(程序会自动将func()这个函数转换为Runnable接口方法的实现,并实例化这个Runnable接口然后传给Thead类?)var th = new java.lang.Thread(func);th.start();

Overload Resolution 重载解析

ava方法可以通过参数类型重载。在Java中,在编译时发生重载解析(由javac执行)。从js脚本中调用Java方法时,脚本解释器/编译器需要选择适当的方法。使用JavaScript引擎,您不需要去做任何特别的事情,根据参数类型选择正确的Java重载方法。但是,有时您可能希望或必须要明确选择特定的重载方法。
var out = java.lang.System.out;

// 选择一个特定的println函数
out[“println(java.lang.Object)”](“hello”);
关于JavaScript的Java方法重载解析的更多细节请参见 Java Method Overloading and LiveConnect 3

Implementing Your Own Script Engine 实现你自己的脚本引擎

我们不会详细介绍JSR-223兼容脚本引擎的实现。 最起码,您需要实现javax.script.ScriptEngine和javax.script.ScriptEngineFactory接口。 抽象类javax.script.AbstractScriptEnginep为ScriptEngine接口的几种方法提供了有用的默认值。

References 参考

● JSR-223 Scripting for the Java Platform
● Java Method Overloading and LiveConnect 3
● Rhino:JavaScript for Java
● Scripting Java (from JavaScript)

阅读全文
2 0
原创粉丝点击