目录
- 一、什么是脚本引擎
- 1. Java ScriptEngine优缺点
- 二、Nashorn JavaScript 引擎
- 三、如何使用Nashorn
- 1. java代码中使用 nashorn
- 2. 向Java传递数据或者从Java传出数据
- 2.1 在 JavaScript 端调用 Java 方法
- 四、常用类总结
- 1. ScriptEngine、ScriptEngineManager
- 2. CompiledScript
- 3. Bindings
- 4. ScriptContext
- 五、参考
一、什么是脚本引擎
使得Java应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在Java平台上调用各种脚本语言的目的。
Java脚本API是连通Java平台和脚本语言的桥梁。
可以把一些复杂易变的业务逻辑交给脚本语言处理,这又大大提高了开发效率。
1. Java ScriptEngine优缺点
优点:可以执行完整的JS方法,并且获取返回值;在虚拟的Context中执行,无法调用系统操作和IO操作,非常安全;可以有多种优化方式,可以预编译,编译后可以复用,效率接近原生Java;所有实现ScriptEngine接口的语言都可以使用,并不仅限于JS,如Groovy,Ruby等语言都可以动态执行。
缺点:无法调用系统和IO操作 ,也不能使用相关js库,只能使用js的标准语法。更新:可以使用scriptengine.put()将Java原生Object传入Context,从而拓展实现调用系统和IO等操作。
二、Nashorn JavaScript 引擎
Nashorn,发音“nass-horn”,是德国二战时一个坦克的命名,同时也是java8新一代的javascript引擎–替代老旧,缓慢的Rhino,符合 ECMAScript-262 5.1 版语言规范。
javascript运行在jvm已经不是新鲜事了,Rhino早在jdk6的时候已经存在,但现在为何要替代Rhino,官方的解释是Rhino相比其他javascript引擎(比如google的V8)实在太慢了,要改造Rhino还不如重写。因此有了Nashorn。
nashorn首先编译javascript代码为java字节码,然后运行在jvm上,底层也是使用invokedynamic命令来执行,所以运行速度很给力。
从JDK 8开始,Nashorn取代Rhino成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的invokedynamic,将JavaScript编译成Java字节码。
官方参考URL: https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/
三、如何使用Nashorn
Nashorn javascript 引擎要么在java程序中以编程的方式使用要么在命令行工具jjs使用,jjs在目录$JAVA_HOME/bin中。如果你准备建立一个jjs的符号链接,如下:
$ cd /usr/bin
$ ln -s $JAVA_HOME/bin/jjs jjs
$ jjs
jjs> print('Hello World');
1. java代码中使用 nashorn
为了在java中执行JavaScript代码,首先使用原先Rhino (旧版Java1.6中来自Mozilla的引擎)中的包javax.script来创建一个nashorn脚本引擎。.
- 把JavaScript代码作为一个字符串来直接执行,也可放入一个js脚本文件中
如:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('Hello World!');");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("script.js"));
2. 向Java传递数据或者从Java传出数据
- 可以将数据作为字符串显式传递
如下的 name变量
ScriptEngineManager scriptEngineManager =
new ScriptEngineManager();
ScriptEngine nashorn =
scriptEngineManager.getEngineByName("nashorn");
String name = "Olli";
nashorn.eval("print('" + name + "')");
- 可以在Java中传递绑定,它们是可以从JavaScript引擎内部访问的全局变量
int valueIn = 10;
SimpleBindings simpleBindings = new SimpleBindings();
simpleBindings.put("globalValue", valueIn);
nashorn.eval("print (globalValue)", simpleBindings);
- JavaScript eval的求值结果将会从引擎的“eval”方法返回
Integer result = (Integer) nashorn.eval("1 + 2");
assert(result == 3);
2.1 在 JavaScript 端调用 Java 方法
在 JavaScript 中调用 Java 方法很简单。首先我们定义一个静态的 Java 方法:
static String fun1(String name) {
System.out.format("Hi there from Java, %s", name);
return "greetings from java";
}
JavaScript 可通过 Java.type API 来引用 Java 类。这跟在 Java 类中引入其他类是类似的。当定义了 Java 类型后我们可直接调用其静态方法 fun1() 并打印结果到 sout。因为方法是静态的,所以我们无需创建类实例。
var MyJavaClass = Java.type('my.package.MyJavaClass');
var result = MyJavaClass.fun1('John Doe');
print(result);
四、常用类总结
1. ScriptEngine、ScriptEngineManager
代码注释如下:
/**
* <code>ScriptEngine</code> is the fundamental interface whose methods must be
* fully functional in every implementation of this specification.
* <br><br>
* These methods provide basic scripting functionality. Applications written to this
* simple interface are expected to work with minimal modifications in every implementation.
* It includes methods that execute scripts, and ones that set and get values.
* <br><br>
* The values are key/value pairs of two types. The first type of pairs consists of
* those whose keys are reserved and defined in this specification or by individual
* implementations. The values in the pairs with reserved keys have specified meanings.
* <br><br>
* The other type of pairs consists of those that create Java language Bindings, the values are
* usually represented in scripts by the corresponding keys or by decorated forms of them.
*
* @author Mike Grogan
* @since 1.6
*/
public interface ScriptEngine {
ScriptEngine 是个接口类,它规范了包括执行脚本的方法,以及set和get值的方法。
demo:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
public class JSTesterCallFunctions {
public static void main(String[] args){
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
try{
for(int i = 0; i < args.length; i++){
File file = new File(args[i]).getAbsoluteFile();
System.out.println("args[" +i+ "] is" + file);
if(file.isFile()){
String strJSName = args[i];// For Jar
nashorn.eval(new FileReader(strJSName ));
Object eval = nashorn.eval("make('" + strJSName + "')");// 传递参数strJSName到JS脚本
else{
System.out.println("Java运行命令参数非法,请检查!");
}
}
} catch (ScriptException | FileNotFoundException e) {
System.out.println("Error executing script: " + e.getMessage());
}
}
}
ScriptEngineManager是ScriptEngine的工厂,实例化该工厂的时候会加载可用的所有脚本引擎。从工厂中创建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType来得到,只要参数名字能对上。执行脚本调用eval方法即可(效果等同于javascript中的eval)。
2. CompiledScript
把脚本编译成java字节码。可以让脚本重复执行,无需重新解析脚本。每个compiledScript都与一个scripteEngine——一个对一个eval的调用相关联。
/**
* Extended by classes that store results of compilations. State
* might be stored in the form of Java classes, Java class files or scripting
* language opcodes. The script may be executed repeatedly
* without reparsing.
* <br><br>
* Each <code>CompiledScript</code> is associated with a <code>ScriptEngine</code> -- A call to an <code>eval</code>
* method of the <code>CompiledScript</code> causes the execution of the script by the
* <code>ScriptEngine</code>. Changes in the state of the <code>ScriptEngine</code> caused by execution
* of the <code>CompiledScript</code> may visible during subsequent executions of scripts by the engine.
*
* @author Mike Grogan
* @since 1.6
*/
public abstract class CompiledScript {
使用如下:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scriptEngine = manager.getEngineByName( "javascript" );
Bindings scope = scriptEngine.getBindings( ScriptContext.ENGINE_SCOPE );
scope.put( "TransformationName", "xxx" );
CompiledScript startScript = ( (Compilable) scriptEngine ).compile( strStartScript );
startScript.eval( scope );
3. Bindings
如下源码,Bindings就是一个继承Map接口的接口,所有key都是字符串。
/**
* A mapping of key/value pairs, all of whose keys are
* <code>Strings</code>.
*
* @author Mike Grogan
* @since 1.6
*/
public interface Bindings extends Map<String, Object> {
ScriptEngine 有一个getBindings方法,返回Bindings 类型。该方法:
返回命名值的作用域。
可能的范围是:
- scriptcontext.global_scope:表示全局的命名值集范围
- scriptcontext.engine_scope: 表示状态的命名值集是scripteEngine
/**
* Returns a scope of named values. The possible scopes are:
* <br><br>
* <ul>
* <li><code>ScriptContext.GLOBAL_SCOPE</code> - The set of named values representing global
* scope. If this <code>ScriptEngine</code> is created by a <code>ScriptEngineManager</code>,
* then the manager sets global scope bindings. This may be <code>null</code> if no global
* scope is associated with this <code>ScriptEngine</code></li>
* <li><code>ScriptContext.ENGINE_SCOPE</code> - The set of named values representing the state of
* this <code>ScriptEngine</code>. The values are generally visible in scripts using
* the associated keys as variable names.</li>
* <li>Any other value of scope defined in the default <code>ScriptContext</code> of the <code>ScriptEngine</code>.
* </li>
* </ul>
* <br><br>
* The <code>Bindings</code> instances that are returned must be identical to those returned by the
* <code>getBindings</code> method of <code>ScriptContext</code> called with corresponding arguments on
* the default <code>ScriptContext</code> of the <code>ScriptEngine</code>.
*
* @param scope Either <code>ScriptContext.ENGINE_SCOPE</code> or <code>ScriptContext.GLOBAL_SCOPE</code>
* which specifies the <code>Bindings</code> to return. Implementations of <code>ScriptContext</code>
* may define additional scopes. If the default <code>ScriptContext</code> of the <code>ScriptEngine</code>
* defines additional scopes, any of them can be passed to get the corresponding <code>Bindings</code>.
*
* @return The <code>Bindings</code> with the specified scope.
*
* @throws IllegalArgumentException if specified scope is invalid
*
*/
public Bindings getBindings(int scope);
语言绑定对象实际上就是一个简单的哈希表,用来存放和获取需要共享的数据,其定义的接口为javax.script.Bindings,继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象,不同语言绑定对象的作用域不同。ScriptEngine类提供out和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。
使用默认的语言绑定对象:
public void useDefaultBinding() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
engine.put("name", "Alex");
engine.eval("var message = 'Hello, ' + name;");
engine.eval("println(message);");
Object obj = engine.get("message");
System.out.println(obj);
}
可以自定义语言绑定对象(如语言绑定对象中包含程序自己独有的数据等情形……):
public void useCustomBinding() throws ScriptException {
ScriptEngine engine = getJavaScriptEngine();
Bindings bindings = new SimpleBindings();
bindings.put("hobby", "playing games");
engine.eval("println('I like ' + hobby);", bindings);
}
4. ScriptContext
使用如下:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scriptEngine = manager.getEngineByName( "javascript" );
Bindings scope = scriptEngine.getBindings( ScriptContext.ENGINE_SCOPE );
scope.put( "TransformationName", "xxx" );
ScriptContext源码注释如下:
该接口的实现类用于连接脚本引擎
在宿主应用程序中使用对象(如作用域绑定)。每个作用域都是一组
其值可以使用。scriptcontex.脚本上下文暴露读和写,即用于脚本引擎的输入和输出。
/**
* The interface whose implementing classes are used to connect Script Engines
* with objects, such as scoped Bindings, in hosting applications. Each scope is a set
* of named attributes whose values can be set and retrieved using the
* <code>ScriptContext</code> methods. ScriptContexts also expose Readers and Writers
* that can be used by the ScriptEngines for input and output.
*
* @author Mike Grogan
* @since 1.6
*/
public interface ScriptContext {
五、参考
Java 8 的 Nashorn 脚本引擎教程
参考URL:
Java新特性之Nashorn的实例详解
参考URL:
Java 8 Nashorn 教程
参考URL:
[推荐]Nashorn——在JDK 8中融合Java与JavaScript之力
参考URL:
官方参考URL: https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/ java – 每个线程应该使用单独的ScriptEngine和CompiledScript实例吗?
参考URL: http://www.voidcn.com/article/p-czfxznxr-bsx.html