JDK中內嵌JS引擎介紹及使用

one 發表於 2022-12-04

原文: JDK中內嵌JS引擎介紹及使用 - Stars-One的雜貨小窩

最近研究閱讀這個APP,其主要功能就是透過一個個書源,從而實現移動端閱讀的體驗

比如說某些線上小說閱讀網站,會加上相應的廣告,從而影響使用者閱讀體驗,於是閱讀這個APP就是做了類似淨化閱讀體驗

但是小說閱讀網站千千萬萬,如果去適配每個小說閱讀網站,豈不是累死,且作者也會有被髮律師函的危險,於是作者提供了對應的工具,允許各位使用者可自定義書源,閱讀APP則透過匯入書源,即可實現對某個小說閱讀網站的支援

這裡說的書源,實際上就是JS指令碼程式碼,作者本質上是使用了rhino這個Java的JS引擎技術來實現的

介紹

JDK1.6和JDK1.7採用Rhino。Nashorn 支援 ECMAScript 5.1 規範,使用基於 JSR 292 的新語言特性,其中包含在 JDK 7 中引入的 invokedynamic,將 JavaScript 編譯成 Java 位元組碼。

從JDK1.8開始,Java採用Nashorn作為嵌入式 JavaScript 引擎。

本質上,都是通用標準ECMAScriptJS規範,沒啥過多的區別

JDK內建的方式,其實也就是將對應JS引擎的jar包一起整合在Java環境裡了,如果想使用新版本的JS引擎,可以去對應的JS引擎上找最新版本的jar包或透過maven來引用依賴即可

rhino目前是由火狐瀏覽器團隊開發的,使用Java寫的一個JS引擎,目前也是在更新,如果想要更新,而不想更新JDK的話,可以直接使用maven引入最新的jar包即可

Nashorn之前是oracle團隊在開發,現在看github的話,是有openjdk團隊在維護

而在Android平臺方面,由於平臺對JVM進行了調整,所以預設是不支援的,但是好訊息的是,有大神將移植到了Android平臺上,具體可以檢視APISENSE/rhino-android,需要的話可以直接引入依賴即可

不過作者寫的使用文件不太清晰,這裡我比較推薦使用閱讀APP開發者的gedoor/rhino-android,實際上也是基於APISENSE/rhino-android進行了一定的調整,使用起來和Java內建的步驟是一樣的

可能有同學就有疑惑了,使用這個JS引擎能有什麼作用呢?

這裡就舉個例子,有個加密方法,是透過js去實現的,但是現在如果讓我們想要獲取到加密的結果,得透過閱讀JS原始碼,看懂加密思路後再使用Java程式碼重現實現,是不是十分的複雜?

但如果採用JS引擎,我們只需要將對應的JS方法程式碼拿到,之後我們只需要傳引數,透過JS引擎執行加密過程,即可得到加密後的引數了

PS: 最後,注意一下,上述說到的兩種JS引擎,只支援部分ES6特性,所有,如果你的JS程式碼有ES6特性的,可能執行的時候會報錯!

下文以JDK8內建的JS引擎為例,講解一下使用

使用

1.基本使用

首先,我們需要透過ScriptEngineManager物件獲取JS指令碼引擎engine物件,之後透過engine.eval()方法來執行我們需要的js程式碼

// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
val result = engine.eval("""
    var num  = 5+2;
    num
""".trimIndent())
println(result)

PS: 如果是gedoor/rhino-android,getEngineByName()方法裡面傳rhino這個字串!

2.獲取JS變數數值

比較多的情況就是,我們需要透過JS去執行邏輯,之後得到返回的結果,有以下2種方式獲取數值

  1. 透過eval()方法的返回值(如上面基本使用的示例程式碼),可以理解為在瀏覽器的控制檯執行js程式碼後的控制檯會輸出的數值
  2. 透過作用域變數

作用域變數的方式程式碼如下:

// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
//宣告一個變數
engine.put("finalResult","")

//註冊一個js方法
engine.eval("""
    function add(a,b){
        return a + b 
    }
""".trimIndent())

//執行獲取結果,賦予finalResult資料
engine.eval("finalResult = add(2,3)")
println(engine["finalResult"])

或者直接在js中宣告變數finalResult,如下程式碼:

// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")

//註冊一個js方法
engine.eval("""
    function add(a,b){
        return a + b 
    }
""".trimIndent())

//執行獲取結果
engine.eval("var finalResult = add(2,3)")
println(engine["finalResult"])

3.JS使用Java型別數值

// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
val file = File("D:\\temp\\qrcode.gif")
//將檔案的物件設定為JS變數
engine.put("myFile",file)

//註冊一個js方法
engine.eval("""
    print(myFile.getPath());
""".trimIndent())

上面js中的print實際上也是Java提供的方法,而myFile則是我們Java中的一個File物件,JS程式碼中可以使用這個物件及相應的Java方法

參考