Nashorn JavaScript引擎是Java SE 8 的一部分,并且和其它独立的引擎例如 Google V8(用于Google Chrome和Node.js的引擎)互相竞争。Nashorn通过在JVM上,以原生方式运行动态的JavaScript代码来扩展Java的功能。
使用Nashorn
Java代码中简单的HelloWorld如下所示:
1 | ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); |
为了在Java中执行JavaScript,你首先要通过javax.script包创建脚本引擎。
JavaScript代码既可以通过传递JavaScript代码字符串,也可以传递指向你的JS脚本文件的FileReader
来执行:
1 | ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); |
Nashorn JavaScript基于 ECMAScript 5.1,但是它的后续版本会对ES6提供支持:
Nashorn的当前策略遵循ECMAScript规范。当我们在JDK8中发布它时,它将基于ECMAScript 5.1。Nashorn未来的主要发布基于ECMAScript 6。
在Java中调用JavaScript函数
Nashorn 支持从Java代码中直接调用定义在脚本文件中的JavaScript函数。你可以将Java对象传递为函数参数,并且从函数返回数据来调用Java方法。
1 | var fun1 = function(name) { |
ScriptEngine
内置了invokeFunction
方法,提调用javascript函数并返回结果。
1 | ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); |
##在JavaScript中调用Java方法
在JavaScript中调用Java方法十分容易。我们首先需要定义一个Java静态方法。
1 | static String fun1(String name) { |
Java类可以通过Java.type
API扩展在JavaScript中引用。它就和Java代码中的import类似。只要定义了Java类型,我们就可以自然地调用静态方法fun1(),然后像sout打印信息。由于方法是静态的,我们不需要首先创建实例。
1 | var MyJavaClass = Java.type('my.package.MyJavaClass'); |
在使用JavaScript原生类型调用Java方法时,Nashorn 如何处理类型转换?让我们通过简单的例子来弄清楚。
下面的Java方法简单打印了方法参数的实际类型:
1 | static void fun2(Object object) { |
为了理解背后如何处理类型转换,我们使用不同的JavaScript类型来调用这个方法:
1 | MyJavaClass.fun2(123); |
JavaScript原始类型转换为合适的Java包装类,而JavaScript原生对象会使用内部的适配器类来表示。要记住jdk.nashorn.internal
中的类可能会有所变化,所以不应该在客户端面向这些类来编程。
##ScriptObjectMirror
在向Java传递原生JavaScript对象时,你可以使用ScriptObjectMirror
类,它实际上是底层JavaScript对象的Java表示。ScriptObjectMirror
实现了Map
接口,位于jdk.nashorn.api
中。这个包中的类可以用于客户端代码。
下面的例子将参数类型从Object
改为ScriptObjectMirror
,所以我们可以从传入的JavaScript对象中获得一些信息。
1 | static void fun3(ScriptObjectMirror mirror) { |
当向这个方法传递对象(哈希表)时,在Java端可以访问其属性:
1 | MyJavaClass.fun3({ |
我们也可以在Java中调用JavaScript的成员函数。让我们首先定义JavaScript Person
类型,带有属性firstName
和 lastName
,以及方法getFullName
。
1 | function Person(firstName, lastName) { |
JavaScript方法getFullName
可以通过callMember()
在ScriptObjectMirror
上调用。
1 | static void fun4(ScriptObjectMirror person) { |
当向Java方法传递新的Person
时,我们会在控制台看到预期的结果:
1 | var person1 = new Person("Peter", "Parker"); |
实战
通过使用promise实现服务端渲染。
polyfill
由于当前Nashorn基于es5,不支持部分es6对象,我们需要引入polyfill文件,nashorn-polyfill。
该polyfill通过java与JavaScript的结合使用,使Nashorn支持console
, process
, Blob
, Promise
等对象和setTimeout
, clearTimeout
, setInterval
, clearInterval
等函数。
Nashorn工具类
实例化工具类,通过该类操作Javascript对象。
1 | public class NashornHelper { |
实例化工具类
1 | NashornHelper engine = NashornHelper.getInstance(); |
执行调用javasript,java中获取promise
对象
1 | ScriptObjectMirror promise = (ScriptObjectMirror) engine.callRender("ssr_render"); |
执行promise
的then
方法,等待执行完成并回调
1 | promise.callMember("then", fnResolve); |
回调函数
1 | private Consumer<Object> fnResolve = object -> { |
最后结果已字符串形式存在html
中,可将其渲染到页面中.