Jvm上如何執行其他語言?JSR223規範最詳細講解

鉑賽東發表於2022-12-07

在Java的平臺裡,其實是可以執行其他的語言的。包括且不僅限於jvm發展出來的語言。

有的同學可能會說,在java專案裡執行其他語言,這不吃飽了撐著麼,java體系那麼龐大,各種工具一應俱全,放著好好的java不寫,還要去執行其他語言幹嘛。

寫java的都知道,java是需要事先編譯的,這意味著你很難去在執行中改變編譯好的class資訊,除非你用位元組碼等技術棧,但是這也是需要很大的成本的。要想在執行中很方便的改變業務邏輯,其實用java去執行其他的指令碼語言是一個好辦法。況且有的指令碼語言有著比java更簡潔的語法特性。

有興趣的小夥伴也可以看看之前的這篇文章:Java專案有可能做到所有的程式碼邏輯均可熱部署嗎?

在java中執行其他語言,可能你會覺得這應該很複雜,需要去學習每種語言包相關的api。

筆者是開源框架LiteFlow的作者,在規則引擎LiteFlow中實踐並支援了許多的其他語言,如groovy,js,python,lua等。

我可以負責任的說,在Java平臺中呼叫其他指令碼語言,其實一點都不復雜,你無需關心每種語言的實際api。

這一切都歸功於一個規範:JSR223

相信有大部分人沒聽過這個Java平臺的規範。

JSR223規範最初在Java6平臺被提出,提供了一套標準的API為指令碼語言的執行提供了內建支援。

也就是說,你只要熟悉這一套API就能執行大部分的指令碼語言。

而且這套API的使用也是非常方便的,幾個核心方法仔細看個10分鐘就能明白如何使用。

來個最簡單的例子:

//獲得javascript的指令碼引擎
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("javascript");
//進行指令碼編譯
String script = "function process(){\n" +
  "var a=10\n" +
  "var b=3\n" +
  "return a*b-c\n" +
  "}\n" +
  "process()";
CompiledScript compiledScript = ((Compilable) scriptEngine).compile(script);
//繫結java的引數
Bindings bindings = new SimpleBindings();
bindings.put("c", 5);
//執行並列印結果
Object result = compiledScript.eval(bindings);
System.out.println(result);

上述程式碼演示的是用JSR223 API去執行javascript語言。值得一提的是,java內建了javascript引擎,你無需引入任何第三方包依賴就可以獲得這個引擎。

整個過程分4塊,分別是獲得引擎,指令碼編譯,繫結java引數,執行。

在實際業務中,建議在系統啟動的時候去編譯指令碼,然後把編譯好的指令碼物件compiledScript 物件給快取起來,因為編譯過程相對比較耗時,執行時每次去編譯是個糟糕的設計。

如果在執行中改變了指令碼,只需要重新去編譯這個指令碼並快取其編譯後的物件即可。

你只需要掌握以上程式碼,那幾乎就已經掌握了JSR223規範的使用了。是不是很簡單?

如果你想換成groovy指令碼語言,那你需要依賴第三方依賴

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-jsr223</artifactId>
  	<version>3.0.8</version>
</dependency>

然後在上述的程式碼裡獲得引擎這塊換成groovy即可:

...
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("groovy");
...

如果你想換成python,需要依賴第三方依賴

<dependency>
    <groupId>org.python</groupId>
    <artifactId>jython-standalone</artifactId>
  	<version>2.7.3</version>
</dependency>

然後在上述的程式碼裡獲得引擎這塊換成python即可:

...
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("python");
...

看到這,是不是對利用JSR223規範如何執行指令碼恍然大悟了呢。

其實現在很多的語言在java平臺都推出了自己的java三方執行依賴包,而且很多的包都支援了JSR223規範。只要支援了JSR223規範的語言,都可以利用上述的程式碼來執行。

JSR223規範的API可以支援java和其他語言的繫結和互通,一個java物件透過bindings物件也是可以傳到指令碼語言中的,在指令碼語言中,你可以獲得java的物件,來進行呼叫其方法和進行邏輯計算。

不過不同的語言呼叫運算子也許有所不同,比如groovy,javascript都是透過點運算子,和java很像,筆者在LiteFlow裡新支援了Lua指令碼,Lua指令碼的對java物件的運算子是冒號。所以當你的專案支援相關的指令碼語言之前,你先要熟悉下相關語言的語法。

用指令碼語言來擔當java平臺中經常需要變動的部分是一個不錯的選擇。關鍵原因是指令碼語言的編譯,執行完全是動態的,這意味著你可以在java執行中改變指令碼,重新編譯,並執行。利用此特性來進行變相的熱部署。

LiteFlow就是這樣一款能夠讓你用多種指令碼語言來定義你邏輯的規則引擎框架,這其中也利用了JSR223的規範API,不僅能用指令碼來編寫邏輯,還能進行規則編排。

專案官網:

https://liteflow.yomahub.com

gitee託管倉庫:

https://gitee.com/dromara/liteFlow

希望大家都能從JSR223規範中找到一些設計你們相關業務系統的靈感。

相關文章