小編給大家分享一下Java 8中Nashorn腳本引擎有什么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司 - 成都服務(wù)器托管,四川服務(wù)器租用,成都服務(wù)器租用,四川網(wǎng)通托管,綿陽(yáng)服務(wù)器托管,德陽(yáng)服務(wù)器托管,遂寧服務(wù)器托管,綿陽(yáng)服務(wù)器托管,四川云主機(jī),成都云主機(jī),西南云主機(jī),成都服務(wù)器托管,西南服務(wù)器托管,四川/成都大帶寬,機(jī)柜大帶寬,四川老牌IDC服務(wù)商
Nashorn JavaScript 引擎是Java SE 8的一部分,它與其它像Google V8 (它是Google Chrome 和Node.js的引擎)的獨(dú)立引擎相互競(jìng)爭(zhēng)。 Nashorn 擴(kuò)展了Java在JVM上運(yùn)行動(dòng)態(tài)JavaScript腳本的能力。
在接下來(lái)的大約15分鐘里,您將學(xué)習(xí)如何在 JVM 上動(dòng)態(tài)運(yùn)行 JavaScript。 通過(guò)一些簡(jiǎn)短的代碼示例演示最近 Nashorn 的語(yǔ)言特性。 學(xué)習(xí) Java 與 JavaScript 的相互調(diào)用。***包括如何在日常的 Java 業(yè)務(wù)中整合動(dòng)態(tài)腳本。
使用Nashorn
Nashorn javascript 引擎要么在java程序中以編程的方式使用要么在命令行工具jjs使用,jjs在目錄$JAVA_HOME/bin
中。如果你準(zhǔn)備建立一個(gè)jjs的符號(hào)鏈接,如下:
$ cd /usr/bin $ ln -s $JAVA_HOME/bin/jjs jjs $ jjs jjs> print('Hello World');
本教程關(guān)注的是在java代碼中使用 nashorn ,所以我們現(xiàn)在跳過(guò)jjs。用java代碼來(lái)一個(gè)簡(jiǎn)單的 HelloWorld示例,如下:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print('Hello World!');");
為了在java中執(zhí)行JavaScript代碼,首先使用原先Rhino (舊版Java中來(lái)自Mozilla的引擎)中的包javax.script來(lái)創(chuàng)建一個(gè)nashorn腳本引擎。.
既可以向上面那樣把JavaScript代碼作為一個(gè)字符串來(lái)直接執(zhí)行,也可放入一個(gè)js腳本文件中,如:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js"));
Nashorn javascript是基于 ECMAScript 5.1 ,但nashorn后續(xù)版本將支持 ECMAScript 6:
當(dāng)前Nashorn的策略是遵循ECMAScript規(guī)范。 當(dāng)我們發(fā)布JDK 8時(shí),我們將實(shí)現(xiàn)ECMAScript 5.1標(biāo)準(zhǔn)。后續(xù)的 Nashorn的版本將實(shí)現(xiàn) ECMAScript Edition 6標(biāo)準(zhǔn)。
Nashorn定義了很多語(yǔ)言和擴(kuò)展了 ECMAScript標(biāo)準(zhǔn)的API 。接下來(lái)我們看看java與JavaScript的通信。
Java調(diào)用Javascript 函數(shù)
Nashorn 支持java代碼直接調(diào)用定義在腳本文件中JavaScript函數(shù)。你可以把java對(duì)象作為函數(shù)的參數(shù)且在調(diào)用函數(shù)的java方法中接收返回的數(shù)據(jù)。
如下的JavaScript代碼將會(huì)在java端調(diào)用:
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)); };
為了調(diào)用函數(shù),你首先得把腳本引擎轉(zhuǎn)換為 Invocable。NashornScriptEngine
實(shí)現(xiàn)了 Invocable 接口且定義一個(gè)調(diào)用JavaScript函數(shù)的方法 invokeFunction
,傳入函數(shù)名即可。
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
上述代碼的執(zhí)行將在控制臺(tái)打印三行信息。調(diào)用 print 函數(shù)將輸出內(nèi)容通過(guò)管道送到 System.out 控制臺(tái),因此我們首先看到的是 JavaScript打印的信息。
現(xiàn)在我們通過(guò)傳遞任意的 Java 對(duì)象去調(diào)用第二個(gè)函數(shù):
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]
你可以傳遞任意 Java 對(duì)象而不會(huì)在 JavaScript 這邊丟失類型信息。因?yàn)槟_本本身是在 JVM 虛擬機(jī)中執(zhí)行的,我們可以完全利用 nashorn 引擎的 Java API 和外部庫(kù)的強(qiáng)大功能。
在 JavaScript 端調(diào)用 Java 方法
在 JavaScript 中調(diào)用 Java 方法很簡(jiǎn)單。首先我們定義一個(gè)靜態(tài)的 Java 方法:
static String fun1(String name) { System.out.format("Hi there from Java, %s", name); return "greetings from java"; }
JavaScript 可通過(guò) Java.type API 來(lái)引用 Java 類。這跟在 Java 類中引入其他類是類似的。當(dāng)定義了 Java 類型后我們可直接調(diào)用其靜態(tài)方法 fun1() 并打印結(jié)果到 sout。因?yàn)榉椒ㄊ庆o態(tài)的,所以我們無(wú)需創(chuàng)建類實(shí)例。
var MyJavaClass = Java.type('my.package.MyJavaClass'); var result = MyJavaClass.fun1('John Doe'); print(result); // Hi there from Java, John Doe // greetings from java
當(dāng)調(diào)用java 方法時(shí),Nashorn怎樣處理原生JavaScript類型與java類型轉(zhuǎn)換?讓我們用一個(gè)簡(jiǎn)單的例子來(lái)發(fā)現(xiàn)。
下面的java方法簡(jiǎn)單打印實(shí)際的類方法參數(shù)的類型:
static void fun2(Object object) { System.out.println(object.getClass()); }
為了解引擎如何處理類型轉(zhuǎn)換,我使用不同JavaScript類型來(lái)調(diào)用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
原始的javascript 類型被轉(zhuǎn)換為適當(dāng)?shù)?nbsp;java 包裝器類。而不是本地javascript對(duì)象內(nèi)部適配器類。請(qǐng)記住,這些類來(lái)自于jdk.nashorn.internal
,所以你不應(yīng)該在客戶端使用這些類:
Anything marked internal will likely change out from underneath you.
ScriptObjectMirror
當(dāng)使用ScriptObjectMirror
把本地JavaScript對(duì)象傳入時(shí),實(shí)際上是有一個(gè)java對(duì)象表示JavaScript 對(duì)象。 ScriptObjectMirror 實(shí)現(xiàn)了接口與jdk.nashorn.api
內(nèi)部的映射。這個(gè)包下的類目的就是用于客戶端代碼使用。
下一個(gè)示例更改參數(shù)類型Object為ScriptObjectMirror,因此我們能獲取到傳入JavaScript中對(duì)象的一些信息:
static void fun3(ScriptObjectMirror mirror) { System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true))); }
當(dāng)我們把傳遞對(duì)象hash到方法中,在Java端就能訪問(wèn)這些屬性:
MyJavaClass.fun3({ foo: 'bar', bar: 'foo' }); // Object: [foo, bar]
我們也可以在Java端調(diào)用JavaScript對(duì)象中的函數(shù)。我們首先定義一個(gè)JavaScript類型 Person,包含屬性 firstName
、lastName
和函數(shù)getFullName。
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; } }
javascript 函數(shù)getFullName
能被 ScriptObjectMirror 的callMember()調(diào)用。
static void fun4(ScriptObjectMirror person) { System.out.println("Full Name is: " + person.callMember("getFullName")); }
當(dāng)我們傳入一個(gè)新的person給java 方法時(shí),我們能在控制臺(tái)看到預(yù)期結(jié)果:
var person1 = new Person("Peter", "Parker"); MyJavaClass.fun4(person1); // Full Name is: Peter Parker
語(yǔ)言擴(kuò)展
Nashorn 定義一系列的語(yǔ)言和擴(kuò)展了 ECMAScript 標(biāo)準(zhǔn)的API。 讓我們直接進(jìn)入***的功能:
類型數(shù)組
原始javascript 數(shù)組時(shí)無(wú)類型的。 Nashorn 運(yùn)行你在JavaScript中使用java數(shù)組:
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[]
數(shù)組的行為像一個(gè)真正的 java int 數(shù)組。 但當(dāng)我們?cè)噲D添加非整數(shù)的值的數(shù)組時(shí),Nashorn 會(huì)執(zhí)行隱式類型轉(zhuǎn)換。 字符串會(huì)自動(dòng)轉(zhuǎn)換為int,這相當(dāng)方便。
集合與For Each
我們可以使用java的集合來(lái)代替數(shù)組。首先定義使用 Java.type
定義一個(gè)java類型,而后根據(jù)需要?jiǎng)?chuàng)建一個(gè)實(shí)例。
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
為了遍歷集合和數(shù)組中的元素,Nashorn 引入了 for each 語(yǔ)句。這就像是 Java 的 for 循環(huán)一樣。
這里是一個(gè)對(duì)集合元素進(jìn)行遍歷的例子,使用的是 :
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 表達(dá)式和 Streams
似乎大家都比較喜歡 Lambda 和 Streams —— Nashorn 也是!雖然 ECMAScript 5.1 中缺少 Java 8 Lambda 表達(dá)式中的緊縮箭頭的語(yǔ)法,但我們可以在接受 Lambda 表達(dá)式的地方使用函數(shù)來(lái)替代。
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
擴(kuò)展類
Java 的類型可以簡(jiǎn)單的通過(guò) Java.extend
進(jìn)行擴(kuò)展,在下個(gè)例子你將在腳本中創(chuàng)建一個(gè)多線程示例:
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
參數(shù)重載
方法和函數(shù)可以使用點(diǎn)符號(hào)或方括號(hào)來(lái)進(jìn)行調(diào)用。
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
在使用重載的參數(shù)來(lái)調(diào)用方法時(shí)可以傳遞可選參數(shù)來(lái)確定具體調(diào)用了哪個(gè)方法,如 println(double)。
Java Beans
我們不需要常規(guī)的用 getter 或者 setter 來(lái)訪問(wèn)類成員屬性,可直接用屬性名簡(jiǎn)單訪問(wèn) Java Bean 中的屬性。例如:
var Date = Java.type('java.util.Date'); var date = new Date(); date.year += 1900; print(date.year); // 2014
函數(shù)語(yǔ)法
如果只是簡(jiǎn)單的一行函數(shù)我們可以不用大括號(hào):
function sqr(x) x * x; print(sqr(3)); // 9
屬性綁定
來(lái)自不同對(duì)象的屬性可以綁定在一起:
function sqr(x) x * x; print(sqr(3)); // 9
字符串處理
我喜歡字符串裁剪.
print(" hehe".trimLeft()); // hehe print("hehe ".trimRight() + "he"); // hehehe
在哪里
以防忘記你在哪里:
print(__FILE__, __LINE__, __DIR__);
Import 的范圍
有時(shí),這在一次性導(dǎo)入多個(gè)java 包時(shí)非常有用。我們可以使用JavaImporter并結(jié)合with,在with塊范圍內(nèi)引用:
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 }
數(shù)組轉(zhuǎn)換
有些包時(shí)可以直接使用而不必利用 Java.type
或JavaImporter引入,如 java.util
:
var list = new java.util.ArrayList(); list.add("s1"); list.add("s2"); list.add("s3");
如下的代碼演示了將java list轉(zhuǎn)換為JavaScript的數(shù)組:
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[]");
調(diào)用父類函數(shù)
在 JavaScript 中訪問(wèn)重載的成員會(huì)有一點(diǎn)點(diǎn)尷尬,因?yàn)?ECMAScript 沒(méi)有類似 Java 的 super 關(guān)鍵字一樣的東西。所幸的是 Nashorn 有辦法解決。
首先我們?cè)?Java 代碼中定義一個(gè)超類:
class SuperRunner implements Runnable { @Override public void run() { System.out.println("super run"); } }
接下來(lái)我們?cè)?JavaScript 中重載 SuperRunner 。創(chuàng)建一個(gè)新的 Runner 實(shí)例時(shí)請(qǐng)注意 Nashorn 的擴(kuò)展語(yǔ)法:其重載成員的語(yǔ)法是參考 Java 的匿名對(duì)象的做法。
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調(diào)用了重載方法
SuperRunner.run()。
在JavaScript中執(zhí)行其它腳本是十分容易的。我們可以load函數(shù)載入本地或遠(yuǎn)程的腳本。
在我的很多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
擴(kuò)展腳本的執(zhí)行是在同一個(gè) JavaScript 上下文中,因此我們可以直接訪問(wèn) underscore 變量。記住腳本的加載可能會(huì)因?yàn)樽兞棵闹丿B導(dǎo)致代碼出問(wèn)題。
我們可以通過(guò)將加載的腳本文件放置到一個(gè)新的全局上下文來(lái)解決這個(gè)問(wèn)題:
loadWithNewGlobal('script.js');
命令行腳本
如果你對(duì)用 Java 編寫(xiě)命令行腳本很感興趣的話,可以試試 Nake 。Nake 是一個(gè)為 Java 8 Nashorn 準(zhǔn)備的簡(jiǎn)單 Make 工具。你可以在 Nakefile 文件中定義任務(wù),然后使用 nake — myTask 來(lái)運(yùn)行任務(wù)。任務(wù)使用 JavaScript 編寫(xiě)并通過(guò) Nashorn 腳本模式運(yùn)行,因此你可以讓你的終端應(yīng)用完全利用 Java 8 API 和其他 Java 庫(kù)強(qiáng)大的功能。
對(duì) Java 開(kāi)發(fā)者而言,編寫(xiě)命令行腳本從來(lái)沒(méi)有如此簡(jiǎn)單過(guò)。
以上是“Java 8中Nashorn腳本引擎有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!