Heim >Java >javaLernprogramm >Detaillierte Erklärung der Nashorn-Skript-Engine für Java8
In diesem Artikel geht es um alle leicht verständlichen Codebeispiele der Nashorn JavaScript-Engine. Die Nashorn-JavaScript-Engine ist Teil von Java SE 8 und konkurriert mit anderen eigenständigen Engines wie Google V8 (der Engine hinter Google Chrome und Node.js). Nashorn erweitert die Fähigkeit von Java, dynamische JavaScript-Skripte auf der JVM auszuführen.
In den nächsten etwa 15 Minuten erfahren Sie, wie Sie JavaScript dynamisch auf der JVM ausführen. Demonstrieren Sie die neuesten Funktionen der Nashorn-Sprache anhand einiger kurzer Codebeispiele. Erfahren Sie, wie Java und JavaScript sich gegenseitig aufrufen. Schließlich erfahren Sie, wie Sie dynamische Skripte in Ihr tägliches Java-Geschäft integrieren.
Die Nashorn-Javascript-Engine wird entweder in einem Java-Programm zur Programmierung oder im Befehlszeilentool jjs verwendet, das sich im Verzeichnis . Wenn Sie einen symbolischen Link zu jjs erstellen möchten, gehen Sie wie folgt vor: $JAVA_HOME/bin
$ cd /usr/bin $ ln -s $JAVA_HOME/bin/jjs jjs $ jjs jjs> print('Hello World');Dieses Tutorial konzentriert sich auf die Verwendung von Nashorn in Java-Code, daher überspringen wir jjs vorerst. Ein einfaches HelloWorld-Beispiel mit Java-Code lautet wie folgt:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print('Hello World!');");Um JavaScript-Code in Java auszuführen, verwenden Sie zunächst das Paket javax.script im ursprünglichen Rhino (die Engine von Mozilla in der alten Version von). Java), um eine Nashorn-Skript-Engine zu erstellen. . kann wie oben direkt als
String ausgeführt werden oder in eine js-Skriptdatei eingefügt werden, wie zum Beispiel:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js"));Nashorn Javascript basiert auf ECMAScript 5.1, aber nachfolgende Versionen von Nashorn werden ECMAScript 6 unterstützen:Die aktuelle Nashorn-Strategie besteht darin, der ECMAScript-Spezifikation zu folgen. Wenn wir JDK 8 veröffentlichen, werden wir den ECMAScript 5.1-Standard implementieren. Nachfolgende Versionen von Nashorn werden den ECMAScript Edition 6-Standard implementieren. Nashorn definiert viele Sprachen und erweitert die Java ruft Javascript-Funktionen auf Nashorn unterstützt Java-Code, um in Skriptdateien definierte JavaScript-Funktionen direkt aufzurufen. Sie können ein Java-Objekt als Parameter an eine Funktion übergeben und die zurückgegebenen Daten in der Java-Methode empfangen, die die Funktion aufruft. Der folgende JavaScript-Code wird auf der Java-Seite aufgerufen:
var fun1 = function(name) { print('Hi there from Javascript, ' + name); return "greetings from javascript"; }; var fun2 = function (object) { print("JS Class Definition: " + Object.prototype.toString.call(object)); };Um die Funktion aufzurufen, müssen Sie zunächst die Skript-Engine auf
umstellen, die das Invocable implementiert Invocable。NashornScriptEngine
SchnittstelleUnd definieren Sie eine Methode zum Aufrufen einer JavaScript-Funktion , übergeben Sie einfach den Funktionsnamen. invokeFunction
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js")); Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("fun1", "Peter Parker"); System.out.println(result); System.out.println(result.getClass()); // Hi there from Javascript, Peter Parker // greetings from javascript // class java.lang.StringDie Ausführung des obigen Codes druckt drei Informationszeilen auf der Konsole. Der Aufruf der Druckfunktion leitet die Ausgabe an die System.out-Konsole weiter, sodass wir als Erstes die von JavaScript gedruckten Informationen sehen. Jetzt rufen wir die zweite Funktion auf, indem wir ein beliebiges Java-Objekt übergeben:
invocable.invokeFunction("fun2", new Date()); // [object java.util.Date] invocable.invokeFunction("fun2", LocalDateTime.now()); // [object java.time.LocalDateTime] invocable.invokeFunction("fun2", new Person()); // [object com.winterbe.java8.Person]Sie können jedes Java-Objekt übergeben, ohne Typinformationen auf der JavaScript-Seite zu verlieren. Da das Skript selbst in der virtuellen JVM-Maschine ausgeführt wird, können wir die Leistungsfähigkeit der Java-API und der externen Bibliotheken der Nashorn-Engine voll ausschöpfen. Java-Methoden auf der JavaScript-Seite aufrufenJava-Methoden auf der JavaScript-Seite aufrufen ist einfach. Zuerst definieren wir eine
statische Java-Methode:
static String fun1(String name) { System.out.format("Hi there from Java, %s", name); return "greetings from java"; }JavaScript kann
var MyJavaClass = Java.type('my.package.MyJavaClass'); var result = MyJavaClass.fun1('John Doe'); print(result); // Hi there from Java, John Doe // greetings from javaWie geht Nashorn mit nativen JavaScript-Typen und Java
Typkonvertierung um, wenn Java-Methoden aufgerufen werden? Finden wir es anhand eines einfachen Beispiels heraus.
Die folgende Java-Methode gibt einfach den tatsächlichen Parametertyp der Klassenmethode aus:static void fun2(Object object) { System.out.println(object.getClass()); }Um zu verstehen, wie die Engine die Typkonvertierung handhabt, habe ich verschiedene JavaScript-Typen verwendet, um die Java-Methode aufzurufen:
MyJavaClass.fun2(123); // class java.lang.Integer MyJavaClass.fun2(49.99); // class java.lang.Double MyJavaClass.fun2(true); // class java.lang.Boolean MyJavaClass.fun2("hi there") // class java.lang.String MyJavaClass.fun2(new Number(23)); // class jdk.nashorn.internal.objects.NativeNumber MyJavaClass.fun2(new Date()); // class jdk.nashorn.internal.objects.NativeDate MyJavaClass.fun2(new RegExp()); // class jdk.nashorn.internal.objects.NativeRegExp MyJavaClass.fun2({foo: 'bar'}); // class jdk.nashorn.internal.scripts.JO4Der ursprüngliche Javascript-Typ wird in die entsprechende Java-Wrapper-Klasse konvertiert. Anstelle der lokalen
Javascript-Objektinternen Adapterklasse. Denken Sie daran, dass diese Klassen von stammen, daher sollten Sie jdk.nashorn.internal
diese Klassen nicht auf der Clientseite verwenden:
当使用ScriptObjectMirror
把本地JavaScript对象传入时,实际上是有一个java对象表示JavaScript 对象。 ScriptObjectMirror 实现了接口与jdk.nashorn.api
内部的映射。这个包下的类目的就是用于客户端代码使用。
下一个示例更改参数类型Object为ScriptObjectMirror,因此我们能获取到传入JavaScript中对象的一些信息:
static void fun3(ScriptObjectMirror mirror) { System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true))); }
当我们把传递对象hash到方法中,在Java端就能访问这些属性:
MyJavaClass.fun3({ foo: 'bar', bar: 'foo' }); // Object: [foo, bar]
我们也可以在Java端调用JavaScript对象中的函数。我们首先定义一个JavaScript类型 Person,包含属性 firstName
、lastName
和函数getFullName。
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; } }
javascript 函数getFullName
能被 ScriptObjectMirror 的callMember()调用。
static void fun4(ScriptObjectMirror person) { System.out.println("Full Name is: " + person.callMember("getFullName")); }
当我们传入一个新的person给java 方法时,我们能在控制台看到预期结果:
var person1 = new Person("Peter", "Parker"); MyJavaClass.fun4(person1); // Full Name is: Peter Parker
Nashorn 定义一系列的语言和扩展了 ECMAScript 标准的API。 让我们直接进入最新的功能:
原始javascript 数组时无类型的。 Nashorn 运行你在JavaScript中使用java数组:
var IntArray = Java.type("int[]"); var array = new IntArray(5); array[0] = 5; array[1] = 4; array[2] = 3; array[3] = 2; array[4] = 1; try { array[5] = 23; } catch (e) { print(e.message); // Array index out of range: 5 } array[0] = "17"; print(array[0]); // 17 array[0] = "wrong type"; print(array[0]); // 0 array[0] = "17.3"; print(array[0]); // 17
int[]
数组的行为像一个真正的 java int 数组。 但当我们试图添加非整数的值的数组时,Nashorn 会执行隐式类型转换。 字符串会自动转换为int,这相当方便。
我们可以使用java的集合来代替数组。首先定义使用 Java.type
定义一个java类型,而后根据需要创建一个实例。
var ArrayList = Java.type('java.util.ArrayList'); var list = new ArrayList(); list.add('a'); list.add('b'); list.add('c'); for each (var el in list) print(el); // a, b, c
为了遍历集合和数组中的元素,Nashorn 引入了 for each 语句。这就像是 Java 的 for 循环一样。
这里是一个对集合元素进行遍历的例子,使用的是 :
var map = new java.util.HashMap(); map.put('foo', 'val1'); map.put('bar', 'val2'); for each (var e in map.keySet()) print(e); // foo, bar for each (var e in map.values()) print(e); // val1, val2
似乎大家都比较喜欢 Lambda 和 Streams —— Nashorn 也是!虽然 ECMAScript 5.1 中缺少 Java 8 Lambda 表达式中的紧缩箭头的语法,但我们可以在接受 Lambda 表达式的地方使用函数来替代。
var list2 = new java.util.ArrayList(); list2.add("ddd2"); list2.add("aaa2"); list2.add("bbb1"); list2.add("aaa1"); list2.add("bbb3"); list2.add("ccc"); list2.add("bbb2"); list2.add("ddd1"); list2 .stream() .filter(function(el) { return el.startsWith("aaa"); }) .sorted() .forEach(function(el) { print(el); }); // aaa1, aaa2
Java 的类型可以简单的通过 Java.extend
进行扩展,在下个例子你将在脚本中创建一个多线程示例:
var Runnable = Java.type('java.lang.Runnable'); var Printer = Java.extend(Runnable, { run: function() { print('printed from a separate thread'); } }); var Thread = Java.type('java.lang.Thread'); new Thread(new Printer()).start(); new Thread(function() { print('printed from another thread'); }).start(); // printed from a separate thread // printed from another thread
方法和函数可以使用点符号或方括号来进行调用。
var System = Java.type('java.lang.System'); System.out.println(10); // 10 System.out["println"](11.0); // 11.0 System.out["println(double)"](12); // 12.0
在使用重载的参数来调用方法时可以传递可选参数来确定具体调用了哪个方法,如 println(double)。
我们不需要常规的用 getter 或者 setter 来访问类成员属性,可直接用属性名简单访问 Java Bean 中的属性。例如:
var Date = Java.type('java.util.Date'); var date = new Date(); date.year += 1900; print(date.year); // 2014
如果只是简单的一行函数我们可以不用大括号:
function sqr(x) x * x; print(sqr(3)); // 9
来自不同对象的属性可以绑定在一起:
var o1 = {}; var o2 = { foo: 'bar'}; Object.bindProperties(o1, o2); print(o1.foo); // bar o1.foo = 'BAM'; print(o2.foo); // BAM
我喜欢字符串裁剪.
print(" hehe".trimLeft()); // hehe print("hehe ".trimRight() + "he"); // hehehe
以防忘记你在哪里:
print(FILE, LINE, DIR);
有时,这在一次性导入多个java 包时非常有用。我们可以使用JavaImporter并结合with,在with块范围内引用:
var imports = new JavaImporter(java.io, java.lang); with (imports) { var file = new File(FILE); System.out.println(file.getAbsolutePath()); // /path/to/my/script.js }
有些包时可以直接使用而不必利用 Java.type
或JavaImporter引入,如 java.util
:
var list = new java.util.ArrayList(); list.add("s1"); list.add("s2"); list.add("s3");
如下的代码演示了将java list转换为JavaScript的数组:
var jsArray = Java.from(list); print(jsArray); // s1,s2,s3 print(Object.prototype.toString.call(jsArray)); // [object Array]
其他的方式:
var javaArray = Java.to([3, 5, 7, 11], "int[]");
在 JavaScript 中访问重载的成员会有一点点尴尬,因为 ECMAScript 没有类似 Java 的 super 关键字一样的东西。所幸的是 Nashorn 有办法解决。
首先我们在 Java 代码中定义一个超类:
class SuperRunner implements Runnable { @Override public void run() { System.out.println("super run"); } }
接下来我们在 JavaScript 中重载 SuperRunner 。创建一个新的 Runner 实例时请注意 Nashorn 的扩展语法:其重载成员的语法是参考 Java 的匿名对象的做法。
var SuperRunner = Java.type('com.winterbe.java8.SuperRunner'); var Runner = Java.extend(SuperRunner); var runner = new Runner() { run: function() { Java.super(runner).run(); print('on my run'); } } runner.run(); // super run // on my run
我们使用Java.super调用了重载方法
SuperRunner.run()。
在JavaScript中执行其它脚本是十分容易的。我们可以load函数载入本地或远程的脚本。
在我的很多web前端中都使用了 Underscore.js ,因此在Nashorn中我们可以重用 Underscore:
load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); var odds = _.filter([1, 2, 3, 4, 5, 6], function (num) { return num % 2 == 1; }); print(odds); // 1, 3, 5
扩展脚本的执行是在同一个 JavaScript 上下文中,因此我们可以直接访问 underscore 变量。记住脚本的加载可能会因为变量名的重叠导致代码出问题。
我们可以通过将加载的脚本文件放置到一个新的全局上下文来解决这个问题:
loadWithNewGlobal('script.js');
如果你对用 Java 编写命令行脚本很感兴趣的话,可以试试 Nake 。Nake 是一个为 Java 8 Nashorn 准备的简单 Make 工具。你可以在 Nakefile 文件中定义任务,然后使用 nake — myTask 来运行任务。任务使用 JavaScript 编写并通过 Nashorn 脚本模式运行,因此你可以让你的终端应用完全利用 Java 8 API 和其他 Java 库强大的功能。
对 Java 开发者而言,编写命令行脚本从来没有如此简单过。
我希望这篇文章对你有用,可以让你轻松理解 Nashorn JavaScript 引擎。更多关于 Nashorn 的信息请阅读 这里, 这里 和 这里. 如果你是要用 Nashorn 编写 Shell 脚本的话可以参考 这里.
过去我也发表了一些 文章 是关于如何在 Nashron 引擎中使用 Backbone.js 模型数据的。如果你想要了解更多 Java 8 的话可以去看看我的文章 Java 8 Tutorial 和 Java 8 Stream Tutorial.
Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung der Nashorn-Skript-Engine für Java8. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!