Cet article concerne tous les exemples de code faciles à comprendre du moteur Nashorn JavaScript. Le moteur JavaScript Nashorn fait partie de Java SE 8 et est en concurrence avec d'autres moteurs autonomes comme Google V8 (qui est le moteur derrière Google Chrome et Node.js). Nashorn étend la capacité de Java à exécuter des scripts JavaScript dynamiques sur la JVM.
Dans les 15 prochaines minutes environ, vous apprendrez comment exécuter dynamiquement JavaScript sur la JVM. Démontrez les fonctionnalités récentes du langage Nashorn avec quelques exemples de codes courts. Découvrez comment Java et JavaScript s'appellent. Enfin, il explique comment intégrer des scripts dynamiques dans votre activité Java quotidienne.
Le moteur javascript Nashorn est utilisé soit dans un programme java à la manière de programmation, soit dans l'outil de ligne de commande jjs, qui se trouve dans le répertoire $JAVA_HOME/bin
. Si vous souhaitez créer un lien symbolique vers jjs, comme suit :
$ cd /usr/bin $ ln -s $JAVA_HOME/bin/jjs jjs $ jjs jjs> print('Hello World');
Ce tutoriel se concentre sur l'utilisation de nashorn dans le code java, nous allons donc ignorer jjs pour l'instant. Un exemple simple de HelloWorld utilisant du code Java est le suivant :
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print('Hello World!');");
Afin d'exécuter du code JavaScript en Java, utilisez d'abord le package javax.script dans le Rhino d'origine (le moteur de Mozilla dans l'ancienne version de Java) pour créer un moteur de script Nashorn. .
peut être exécuté directement sous forme de chaîne comme ci-dessus, ou il peut être placé dans un fichier de script js, tel que :
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js"));
Le javascript Nashorn est basé sur ECMAScript 5.1, mais les versions ultérieures de Nashorn prendront en charge ECMAScript 6 :
La stratégie actuelle de Nashorn consiste à suivre la spécification ECMAScript. Lorsque nous publierons le JDK 8, nous implémenterons la norme ECMAScript 5.1. Les versions ultérieures de Nashorn implémenteront la norme ECMAScript Edition 6.
Nashorn définit de nombreux langages et étend l'API du standard ECMAScript. Nous examinons ensuite la communication entre Java et JavaScript.
Nashorn prend en charge le code Java pour appeler directement les fonctions JavaScript définies dans les fichiers de script. Vous pouvez transmettre un objet Java en tant que paramètre à une fonction et recevoir les données renvoyées dans la méthode Java qui appelle la fonction.
Le code JavaScript suivant sera appelé côté java :
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)); };
Pour appeler la fonction, vous devez d'abord convertir le moteur de script en Invocable。NashornScriptEngine
qui implémente l'Invocable interfaceEt définissez une méthode pour appeler une fonction JavaScript invokeFunction
, transmettez simplement le nom de la fonction.
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.String
L'exécution du code ci-dessus imprimera trois lignes d'informations sur la console. L'appel de la fonction d'impression redirige la sortie vers la console System.out, donc la première chose que nous voyons sont les informations imprimées par JavaScript.
Maintenant, nous appelons la deuxième fonction en passant n'importe quel objet Java :
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]
Vous pouvez passer n'importe quel objet Java sans perdre les informations de type côté JavaScript. Étant donné que le script lui-même est exécuté dans la machine virtuelle JVM, nous pouvons exploiter pleinement la puissance de l'API Java et des bibliothèques externes du moteur Nashorn.
Appeler des méthodes Java côté JavaScript est simple. Nous définissons d’abord une méthode Java statique :
static String fun1(String name) { System.out.format("Hi there from Java, %s", name); return "greetings from java"; }
JavaScript peut référencer la classe Java via l’API Java.type. Cela revient à introduire d'autres classes dans une classe Java. Après avoir défini un type Java, nous pouvons directement appeler sa méthode statique fun1() et imprimer le résultat sur sud. La méthode étant statique, nous n’avons pas besoin de créer une instance de la classe.
var MyJavaClass = Java.type('my.package.MyJavaClass'); var result = MyJavaClass.fun1('John Doe'); print(result); // Hi there from Java, John Doe // greetings from java
Comment Nashorn gère-t-il les types JavaScript natifs et la conversion de type java lors de l'appel de méthodes Java ? Découvrons-le avec un exemple simple.
La méthode Java suivante imprime simplement le type de paramètre de la méthode de classe réelle :
static void fun2(Object object) { System.out.println(object.getClass()); }
Pour comprendre comment le moteur gère la conversion de type, j'ai utilisé différents types JavaScript pour appeler la méthode Java :
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.JO4
Le type javascript d'origine est converti en classe wrapper Java appropriée. Au lieu de la classe d'adaptateur interne objet javascript local. N'oubliez pas que ces classes proviennent de jdk.nashorn.internal
, vous ne devez donc pas utiliser ces classes du côté client :
Tout ce qui est marqué interne changera probablement sous vous.
当使用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.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!