Java Scripting API 使用示例

Java Scripting API 包含一组类和接口,在 javax.script 包中定义。这是一个相对比较小的 Java 包,以 ScriptEngineManager 类作为起点。一个 ScriptEngineManager 对象可以通过 JAR 文件服务发现机制来查找脚本引擎,而实例化 ScriptEngine 对象的解析脚本使用专门的脚本语言编写。更多关于 javax.script 包的详细信息请看 Java SE 规范http://docs.oracle.com/javase/8/docs/api/javax/script/package-summary.html

Nashorn 引擎是 Java SE 开发工具包(JDK)自带的默认 ECMAScript (JavaScript)引擎。Nashorn 引擎是 Oracle 使用纯 Java 开发的,是 OpenJDK 项目的一部分。你可以在下面地址中找到关于 Nashorn 引擎更详细的信息:http://openjdk.java.net/projects/nashorn/

虽然 Nashorn 是 Java Scripting API 默认使用的 ECMAScript 引擎,但你也可以使用其他兼容 JSR 223 的脚本引擎,或者你可以实现自己的引擎。该文档不涉及脚本引擎的实现方法,但最最基础的级别,你必须实现 javax.script.ScriptEngine 和 javax.script.ScriptEngineFactory 接口。抽象类 javax.script.AbstractScriptEngine 提供了很多默认 ScriptEngine 接口的方法实现。

使用 Java Scripting API 的步骤:

  1. 创建一个 ScriptEngineManager 对象.
  2. 从管理器对象中获取 ScriptEngine 对象
  3. 使用脚本引擎的 eval() 方法来执行脚本

下面我们提供了几个例子来展示如何使用 Java Scripting API。为了让例子尽可能的简单,我们没有对执行过程中的异常进行处理。Java Scripting API 的异常有受检查异常和运行时异常两种,这些异常必须进行正确处理。在每个例子中,ScriptEngineManager 类的实例是通过 Nashorn 引擎的 getEngineByName() 方法来获取的。如果指定名称的引擎不存在,该方法会返回 null 。更多关于 Nashorn 引擎的信息请看 Nashorn User’s Guide.

注意:每个 ScriptEngine 对象都有它独有的变量作用域。要使用多个变量作用域请看  Example 8.

示例 1 —— 执行脚本语句

在这个例子中,调用脚本引擎实例的 eval() 方法来执行以字符串标识的 JavaScript 代码。

import javax.script.*;

public class EvalScript {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code
        engine.eval("print('Hello, World')");
    }
}

示例 2 —— 执行脚本文件

在这个例子中, eval() 方法传递了一个 FileReader 对象作为参数,从 scripts.js 文件中读取 JavaScript 代码。通过封装不同的输入流对象作为 reader 来实现从文件、URL 或者其他资源中执行脚本。

import javax.script.*;

public class EvalFile {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code
        engine.eval(new java.io.FileReader("script.js"));
    }
}

示例 3 —— 输出 Java 对象作为全局变量

在这个例子中,我们创建了一个 File 对象并通过 put() 方法暴露给引擎作为一个名为 file 的全局变量。然后在 JavaScript 代码中调用 eval() 方法就可以访问该变量并调用 getAbsolutePath() 方法

注意:

作为变量的 Java 对象的字段访问和方法调用语法取决于脚本语言。该例子使用 JavaScript 语法,和 Java 的类似。

import javax.script.*;
import java.io.*;

public class ScriptVars {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // create File object
        File f = new File("test.txt");

        // expose File object as a global variable to the engine
        engine.put("file", f);

        // evaluate JavaScript code and access the variable
        engine.eval("print(file.getAbsolutePath())");
    }
}

示例 4 —— 调用脚本函数

本例中在 JavaScript 代码中调用 eval() 方法,定义了一个包含单个参数的函数。然后创建 Invocable 对象,并使用其方法 invokeFunction() 来调用这个函数。

注意:

并非所有的脚本引擎都实现了 Invocable 接口。本例中使用 Nashorn 引擎,可以在脚本中调用之前引擎已经定义的函数。

import javax.script.*;

public class InvokeScriptFunction {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code that defines a function with one parameter
        engine.eval("function hello(name) { print('Hello, ' + name) }");

        // create an Invocable object by casting the script engine object
        Invocable inv = (Invocable) engine;

        // invoke the function named "hello" with "Scripting!" as the argument
        inv.invokeFunction("hello", "Scripting!");
    }
}

示例 5 —— 调用脚本对象的方法

本例中在 JavaScript 代码中调用 eval() 方法定义包含一个方法的对象。该对象在脚本中通过脚本引擎的 get() 方法暴露给 Java 应用,然后创建一个 Invocable 对象,并通过 invokeMethod() 方法调用对象的方法。

注意:

并非素有的脚本引擎都实现了 Invocable 接口。本例中使用 Nashorn 引擎,可以调用引擎之前已经定义的脚本方法。

import javax.script.*;

public class InvokeScriptMethod {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code that defines an object with one method
        engine.eval("var obj = new Object()");
        engine.eval("obj.hello = function(name) { print('Hello, ' + name) }");

        // expose object defined in the script to the Java application
        Object obj = engine.get("obj");

        // create an Invocable object by casting the script engine object
        Invocable inv = (Invocable) engine;

        // invoke the method named "hello" on the object defined in the script
        // with "Script Method!" as the argument
        inv.invokeMethod(obj, "hello", "Script Method!");
    }
}

示例 6 —— 实现一个包含脚本函数的 Java 接口

本例中在 JavaScript 代码中调用 eval() 方法来定义一个函数。然后创建一个 Invocable 对象,并通过其getInterface() 方法来创建一个 Runnable 接口对象。接口的方法在脚本函数中实现,这是通过函数名称匹配的方式实现的(本例中 run() 函数用来实现接口对象的 run() 方法)。最后,启动一个新的线程来运行脚本函数。

import javax.script.*;

public class ImplementRunnable {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code that defines a function with one parameter
        engine.eval("function run() { print('run() function called') }");

        // create an Invocable object by casting the script engine object
        Invocable inv = (Invocable) engine;

        // get Runnable interface object
        Runnable r = inv.getInterface(Runnable.class);

        // start a new thread that runs the script
        Thread th = new Thread(r);
        th.start();
        th.join();
    }
}

示例 7 —— 在脚本对象方法中实现 Java 接口

本例中在 JavaScript 代码中调用 eval() 方法来定义一个包含单个方法的对象。该对象在脚本中通过 get() 方法暴露给 Java 应用。然后创建一个 Invocable 对象并通过其 getInterface() 方法来创建一个 Runnable 接口对象。该接口的方法实现是在脚本对象中相同匹配名称的函数中(本例中对象的 run 方法对应接口的 run 方法的实现)。最后启动一个新线程来运行脚本对象的方法。

import javax.script.*;

public class ImplementRunnableObject {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // evaluate JavaScript code that defines a function with one parameter
        engine.eval("var obj = new Object()")
        engine.eval("obj.run = function() { print('obj.run() method called') }");

        // expose object defined in the script to the Java application
        Object obj = engine.get("obj");

        // create an Invocable object by casting the script engine object
        Invocable inv = (Invocable) engine;

        // get Runnable interface object
        Runnable r = inv.getInterface(obj, Runnable.class);

        // start a new thread that runs the script
        Thread th = new Thread(r);
        th.start();
        th.join();
    }
}

示例 8 —— 使用多个变量作用域

本例中脚本引擎的 put() 方法用来设置 x 变量值为 “hello” 字符串对象。紧接着 eval() 方法用来打印默认作用域下的变量值。然后定义一个不同的脚本上下文,并在其作用域下设置相同变量为不同的值(“world” 字符串)。最后在新的脚本上下文中答应该变量,显示为不同的值。

单一的作用域是 javax.script.Bindings 接口的实例。这个接口继承自 java.util.Map<String,Object> 接口。一个作用域相当于是成对的名称和值的列表,其中名称不能为空。javax.script.ScriptContext 接口通过为每个作用域关联一个 Bindings 对象来实现对多个作用域的支持。默认情况下,每个脚本应用有自己独立的上下文。默认的脚本上下文至少有一个作用域,这是通过静态属性 ENGINE_SCOPE 来定义的。脚本上下文可以通过 getScopes() 方法来实现对不同作用域的支持。
import javax.script.*;

public class MultipleScopes {
    public static void main(String[] args) throws Exception {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");

        // set global variable
        engine.put("x","hello");

        // evaluate JavaScript code that prints the variable (x = "hello")
        engine.eval("print(x)");

        // define a different script context
        ScriptContext newContext = new SimpleScriptContext();
        newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
        Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);

        // set the variable to a different value in another scope
        engineScope.put("x", "world");

        // evaluate the same code but in a different script context (x = "world")
        engine.eval("print(x)", newContext);


相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部