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 的步骤:

创建一个 ScriptEngineManager 对象.

从管理器对象中获取 ScriptEngine 对象

使用脚本引擎的 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);

来源:importnew

上一篇: IM 去中心化概念模型与架构设计

下一篇: 如何开始使用 Java 机器学习

分享到: 更多