在某些業(yè)務(wù)場(chǎng)景下,我們可能會(huì)遇到 lua 中要調(diào)用 java 代碼情況,當(dāng)然這個(gè)用 JNI 肯定是可以做到的,但是有更加方便的辦法:LuaJavaBridge(LuaJava)和 LuaJ。
luaj 主要特征
- 可以從 Lua 調(diào)用 Java Class Static Method
- 調(diào)用 Java 方法時(shí),支持 int/float/boolean/String/Lua function 五種參數(shù)類(lèi)型
- 可以將 Lua function 作為參數(shù)傳遞給 Java,并讓 Java 保存 Lua function 的引用
- 可以從 Java 調(diào)用 Lua 的全局函數(shù),或者調(diào)用引用指向的 Lua function
luaj 的功能很簡(jiǎn)單,但對(duì)于集成各種 SDK 來(lái)說(shuō)已經(jīng)完全滿(mǎn)足需求了。
基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶(hù)、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
luaj 用法示例
Java 方法原型:
publicstaticfloatgetNum(floatn){
returnn;
}
lua 調(diào)用示例:
--Java類(lèi)的名稱(chēng)
localclassName="com/xttblog/Test"
--調(diào)用的Java方法名
localmethod='getNum'
--調(diào)用Java方法需要的參數(shù)
localn=10
localargs={
n
}
--調(diào)用Java方法
local_,testStaticMethod=luaj.callStaticMethod(className,method,args)
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶(hù)小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶(hù)、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
luaj 實(shí)現(xiàn)原理
luaj 的核心目標(biāo)有兩個(gè):從 Lua 調(diào)用 Java, 從 Java 調(diào)用 Lua。整理出來(lái)就是如下幾點(diǎn)
- 查找并調(diào)用指定的 Java 方法
- 檢查調(diào)用結(jié)果,并從 Java 方法獲取返回值
- 將 Lua function 作為參數(shù)傳遞給 Java 方法
- 在 Java 方法中調(diào)用 Lua function
查找并調(diào)用指定的 Java 方法
JNI 提供了 FindClass() 方法用于查找指定的 Class,所以 luaj.callStaticMethod() 的第一個(gè)參數(shù)就是要調(diào)用的 Java Class 的完整類(lèi)名稱(chēng)(類(lèi)名稱(chēng)中的“.”要替換為“/”)
。
找到指定 Class 后,利用 JNI 的 GetStaticMethodID() 方法就可以找到這個(gè)類(lèi)的指定靜態(tài)方法,前提是要提供靜態(tài)方法的名稱(chēng)和簽名。
所謂簽名,就是指Java方法的參數(shù)類(lèi)型和返回類(lèi)型定義。方法的簽名就是類(lèi)似(Ljava/lang/String;ZZI)V
這樣的一串描述,通過(guò)字節(jié)碼方式可以查看,如下示例:
關(guān)于 Java 方法簽名的具體定義,可以參考:JNI Type Signatures
。
這里要說(shuō)的是 luaj 可以根據(jù)調(diào)用參數(shù)自動(dòng)猜測(cè)方法簽名所以示例中我們并沒(méi)有寫(xiě)簽名。
示例中指定參數(shù):
localargs={n}
luaj 根據(jù)這 個(gè)參數(shù),會(huì)構(gòu)造出正確的方法簽名。
注意:這里要說(shuō)的是 Lua 里沒(méi)有辦法準(zhǔn)確判斷一個(gè)數(shù)值是整數(shù)還是浮點(diǎn)數(shù),所以 luaj 在猜測(cè)方法簽名時(shí),假定所有的數(shù)值都是浮點(diǎn)數(shù)。所以下面調(diào)用會(huì)報(bào)錯(cuò):
publicstaticintgetNum(intn){
returnn;
}
--Java類(lèi)的名稱(chēng)
localclassName="com/xttblog/Test"
--調(diào)用的Java方法名
localmethod='getNum'
--調(diào)用Java方法需要的參數(shù)
localn=10
localargs={
n
}
--調(diào)用Java方法
local_,testStaticMethod=luaj.callStaticMethod(className,method,args)
這樣是不行的,所以這個(gè)時(shí)候我們要自己定義簽名。
下面給出正確的示例
publicstaticintgetNum(intn){
returnn;
}
--Java類(lèi)的名稱(chēng)
localclassName="com/xttblog/Test"
--調(diào)用的Java方法名
localmethod='getNum'
--調(diào)用Java方法需要的參數(shù)
localn=10
localargs={
n
}
--定義簽名--參數(shù):[I]nteger--返回值:[I]nt
localsig="(I)I"
--調(diào)用Java方法
local_,testStaticMethod=luaj.callStaticMethod(className,method,args,sig)
簽名使用“(依次排列的參數(shù)類(lèi)型)返回值類(lèi)型”的格式,幾個(gè)例子如下:
簽名解釋
()V 參數(shù):無(wú),返回值:無(wú)
(I)V 參數(shù):int,返回值:無(wú)
(Ljava/lang/String;)Z 參數(shù):字符串,返回值:布爾值
(IF)Ljava/lang/String;參數(shù):整數(shù)、浮點(diǎn)數(shù),返回值:字符串
這里列出不同類(lèi)型對(duì)應(yīng)的 Java 簽名字符串:
類(lèi)型名類(lèi)型
I整數(shù),或者Luafunction
F浮點(diǎn)數(shù)
Z布爾值
Ljava/lang/String;字符串
VVoid空,僅用于指定一個(gè)Java方法不返回任何值
Java 方法里接收 Lua function 的參數(shù)必須定義為 int 類(lèi)型
從 Java 方法獲取返回值
luaj 會(huì)檢查調(diào)用結(jié)果,并從 Java 方法獲取返回值。
luaj 調(diào)用 Java 方法時(shí),可能會(huì)出現(xiàn)各種錯(cuò)誤,因此 luaj 提供了一種機(jī)制讓 Lua 調(diào)用代碼可以確定 Java 方法是否成功調(diào)用。
luaj.callStaticMethod()
會(huì)返回兩個(gè)值:
- 當(dāng)成功時(shí),第一個(gè)值為 true,第二個(gè)值是 Java 方法的返回值(如果有)。
- 當(dāng)失敗時(shí),第一個(gè)值為 false,第二個(gè)值是錯(cuò)誤代碼。
下面的代碼展示了如何檢查返回結(jié)果和獲得返回值:
publicstaticintAddTwoNumbers(finalintnumber1,finalintnumber2){
returnnumber1+number2;
}
Lua代碼
localargs={2,3}
localsig="(II)I"
localok,ret=luaj.callStaticMethod(className,"AddTwoNumbers",args,sig)
ifnotokthen
print("luajerror:",ret)
else
print("ret:",ret)--輸出ret:5
end
錯(cuò)誤代碼定義如下:
錯(cuò)誤代碼描述
-1不支持的參數(shù)類(lèi)型或返回值類(lèi)型
-2無(wú)效的簽名
-3沒(méi)有找到指定的方法
-4Java方法執(zhí)行時(shí)拋出了異常
-5Java虛擬機(jī)出錯(cuò)
-6Java虛擬機(jī)出錯(cuò)
將 Lua function 作為參數(shù)傳遞給 Java 方法
Lua 虛擬機(jī)中,Lua function 以值的形式保存。但這個(gè)值無(wú)法直接給 Java 用,所以 luaj 做了一個(gè) Lua function 引用表。當(dāng)一個(gè) Lua function 傳遞給 Java 時(shí),這個(gè) function 對(duì)應(yīng)的值會(huì)被存在引用表中,并獲得一個(gè)唯一的引用 ID (整數(shù))。Java 代碼拿到這個(gè)引用 ID 后,就可以很方便的調(diào)用該 Lua function 了。
所以 Java 方法里接收 Lua function 的參數(shù)必須定義為 int 類(lèi)型。
示例:
publicstaticintgetNum(intn){
returnn;
}
localfunctioncallback(result)
---方法內(nèi)容
end
--Java類(lèi)的名稱(chēng)
localclassName="com/xttblog/Test"
--調(diào)用的Java方法名
localmethod='getNum'
--調(diào)用Java方法需要的參數(shù)
localargs={
callback
}
--定義簽名--參數(shù):[I]nteger--返回值:[I]nt
localsig="(I)I"
--調(diào)用Java方法
local_,testStaticMethod=luaj.callStaticMethod(className,method,args,sig)
另外,LuaJ 也很好用。只需引入 pom。
?然后直接把 lua 代碼當(dāng)做 String 字符串內(nèi)嵌到 Java 代碼中:
StringluaStr="print'hello,world!'";
Globalsglobals=JsePlatform.standardGlobals();
LuaValuechunk=globals.load(luaStr);
chunk.call();
也可以創(chuàng)建一個(gè) login.lua 腳本,內(nèi)容如下:
--無(wú)參函數(shù)
functionhello()
print'hello'
end
--帶參函數(shù)
functiontest(str)
print('datafromjavais:'..str)
return'haha'
end
然后,Java先載入login.lua腳本并編譯,然后再獲取指定名稱(chēng)的函數(shù),無(wú)參的直接使用call()方法調(diào)用,帶參的需要通過(guò)invoke(LuaValue[])傳入?yún)?shù)表:
StringluaPath="res/lua/login.lua";//lua腳本文件所在路徑
Globalsglobals=JsePlatform.standardGlobals();
//加載腳本文件login.lua,并編譯
globals.loadfile(luaPath).call();
//獲取無(wú)參函數(shù)hello
LuaValuefunc=globals.get(LuaValue.valueOf("hello"));
//執(zhí)行hello方法
func.call();
//獲取帶參函數(shù)test
LuaValuefunc1=globals.get(LuaValue.valueOf("test"));
//執(zhí)行test方法,傳入String類(lèi)型的參數(shù)參數(shù)
Stringdata=func1.call(LuaValue.valueOf("I'amfromJava!")).toString();
//打印lua函數(shù)回傳的數(shù)據(jù)
Logger.info("datareturnfromluais:"+data);
運(yùn)行結(jié)果如下:
hello
datafromjavais:I'amfromJava!
八月07,2022525下午com.tw.login.tools.Loggerinfo
信息: lua return data:haha
-
JAVA
+關(guān)注
關(guān)注
19文章
2952瀏覽量
104495 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4286瀏覽量
62337 -
代碼
+關(guān)注
關(guān)注
30文章
4728瀏覽量
68251
原文標(biāo)題:Java與lua互相調(diào)用簡(jiǎn)單教程
文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論