Java与lua互相调用简单教程

简介: Java与lua互相调用简单教程

在某些业务场景下,我们可能会遇到 lua 中要调用 java 代码情况,当然这个用 JNI 肯定是可以做到的,但是有更加方便的办法:LuaJavaBridge(LuaJava)和 LuaJ。

luaj 主要特征

  • 可以从 Lua 调用 Java Class Static Method
  • 调用 Java 方法时,支持 int/float/boolean/String/Lua function 五种参数类型
  • 可以将 Lua function 作为参数传递给 Java,并让 Java 保存 Lua function 的引用
  • 可以从 Java 调用 Lua 的全局函数,或者调用引用指向的 Lua function

luaj 的功能很简单,但对于集成各种 SDK 来说已经完全满足需求了。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

luaj 用法示例

Java 方法原型:

public static float getNum(float n) {
    return n;
}

lua 调用示例:

-- Java 类的名称
local className = "com/xttblog/Test"
-- 调用 的Java 方法名
local method = 'getNum'
-- 调用 Java 方法需要的参数
local n = 10
local args = {
     n
}
-- 调用 Java 方法
local _, testStaticMethod = luaj.callStaticMethod(className, method, args)

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

luaj 实现原理

luaj 的核心目标有两个:从 Lua 调用 Java, 从 Java 调用 Lua。整理出来就是如下几点

  • 查找并调用指定的 Java 方法
  • 检查调用结果,并从 Java 方法获取返回值
  • 将 Lua function 作为参数传递给 Java 方法
  • 在 Java 方法中调用 Lua function

查找并调用指定的 Java 方法

JNI 提供了 FindClass() 方法用于查找指定的 Class,所以 luaj.callStaticMethod() 的第一个参数就是要调用的 Java Class 的完整类名称(类名称中的“.”要替换为“/”)

找到指定 Class 后,利用 JNI 的 GetStaticMethodID() 方法就可以找到这个类的指定静态方法,前提是要提供静态方法的名称和签名。

所谓签名,就是指Java方法的参数类型和返回类型定义。方法的签名就是类似(Ljava/lang/String;ZZI)V这样的一串描述,通过字节码方式可以查看,如下示例:

微信图片_20220908154057.jpg

关于 Java 方法签名的具体定义,可以参考:JNI Type Signatures

这里要说的是 luaj 可以根据调用参数自动猜测方法签名所以示例中我们并没有写签名。

示例中指定参数:

local args = {n}

luaj 根据这 个参数,会构造出正确的方法签名。

注意:这里要说的是 Lua 里没有办法准确判断一个数值是整数还是浮点数,所以 luaj 在猜测方法签名时,假定所有的数值都是浮点数。所以下面调用会报错:

public static int getNum(int n) {
  return n;
}
-- Java 类的名称
local className = "com/xttblog/Test"
-- 调用 的Java 方法名
local method = 'getNum'
-- 调用 Java 方法需要的参数
local n = 10
local args = {
     n
}
-- 调用 Java 方法
local _, testStaticMethod = luaj.callStaticMethod(className, method, args)

这样是不行的,所以这个时候我们要自己定义签名。

下面给出正确的示例

public static int getNum(int n) {
  return n;
}
-- Java 类的名称
local className = "com/xttblog/Test"
-- 调用 的Java 方法名
local method = 'getNum'
-- 调用 Java 方法需要的参数
local n = 10
local args = {
     n
}
-- 定义签名-- 参数: [I]nteger-- 返回值: [I]nt
local sig = "(I)I"
-- 调用 Java 方法
local _, testStaticMethod = luaj.callStaticMethod(className, method, args,sig)

签名使用“(依次排列的参数类型)返回值类型”的格式,几个例子如下:

签名                                         解释
()V                             参数:无,返回值:无
(I)V                            参数:int,返回值:无
(Ljava/lang/String;)Z           参数:字符串,返回值:布尔值
(IF)Ljava/lang/String;          参数:整数、浮点数,返回值:字符串

这里列出不同类型对应的 Java 签名字符串:

类型名                 类型
I                       整数,或者 Lua function
F                       浮点数
Z                       布尔值
Ljava/lang/String;      字符串
V                       Void 空,仅用于指定一个 Java 方法不返回任何值

Java 方法里接收 Lua function 的参数必须定义为 int 类型

从 Java 方法获取返回值

luaj 会检查调用结果,并从 Java 方法获取返回值。

luaj 调用 Java 方法时,可能会出现各种错误,因此 luaj 提供了一种机制让 Lua 调用代码可以确定 Java 方法是否成功调用。

luaj.callStaticMethod()会返回两个值:

  • 当成功时,第一个值为 true,第二个值是 Java 方法的返回值(如果有)。
  • 当失败时,第一个值为 false,第二个值是错误代码。

下面的代码展示了如何检查返回结果和获得返回值:

public static int AddTwoNumbers(final int number1, final int number2) {
  return number1 + number2;
}

Lua代码

local args = {2, 3}
local sig = "(II)I"
local ok, ret = luaj.callStaticMethod(className, "AddTwoNumbers", args, sig)
if not ok then
print("luaj error:", ret)
else
print("ret:", ret) -- 输出 ret: 5
end

错误代码定义如下:

错误代码                            描述
-1                          不支持的参数类型或返回值类型
-2                          无效的签名
-3                          没有找到指定的方法
-4                          Java 方法执行时抛出了异常
-5                          Java 虚拟机出错
-6                          Java 虚拟机出错

将 Lua function 作为参数传递给 Java 方法

Lua 虚拟机中,Lua function 以值的形式保存。但这个值无法直接给 Java 用,所以 luaj 做了一个 Lua function 引用表。当一个 Lua function 传递给 Java 时,这个 function 对应的值会被存在引用表中,并获得一个唯一的引用 ID (整数)。Java 代码拿到这个引用 ID 后,就可以很方便的调用该 Lua function 了。

所以 Java 方法里接收 Lua function 的参数必须定义为 int 类型。

示例:

public static int getNum(int n) {
  return n;
}
local function callback(result)
     ---方法内容
end
-- Java 类的名称
local className = "com/xttblog/Test"
-- 调用 的Java 方法名
local method = 'getNum'
-- 调用 Java 方法需要的参数
local args = {
     callback
}
-- 定义签名-- 参数: [I]nteger-- 返回值: [I]nt
local sig = "(I)I"
-- 调用 Java 方法
local _, testStaticMethod = luaj.callStaticMethod(className, method, args,sig)

另外,LuaJ 也很好用。只需引入 pom。

微信图片_20220908154207.jpg

然后直接把 lua 代码当做 String 字符串内嵌到 Java 代码中:

String luaStr = "print 'hello,world!'";
Globals globals = JsePlatform.standardGlobals();
LuaValue chunk = globals.load(luaStr);
chunk.call();

也可以创建一个 login.lua 脚本,内容如下:

--无参函数
   function hello()
      print 'hello'
   end
--带参函数
   function test(str)
      print('data from java is:'..str)
      return 'haha'
   end

然后,Java先载入login.lua脚本并编译,然后再获取指定名称的函数,无参的直接使用call()方法调用,带参的需要通过invoke(LuaValue[])传入参数表:

String luaPath = "res/lua/login.lua"; //lua脚本文件所在路径
   Globals globals = JsePlatform.standardGlobals();
//加载脚本文件login.lua,并编译
globals.loadfile(luaPath).call();
//获取无参函数hello
LuaValue func = globals.get(LuaValue.valueOf("hello"));
//执行hello方法
func.call();
//获取带参函数test
LuaValue func1 = globals.get(LuaValue.valueOf("test"));
//执行test方法,传入String类型的参数参数
String data = func1.call(LuaValue.valueOf("I'am from Java!")).toString();
   //打印lua函数回传的数据
   Logger.info("data return from lua is:"+data);

运行结果如下:

hello
data from java is:I'am from Java!
八月 07, 2022 5:31:25 下午 com.tw.login.tools.Logger info
信息: lua return data:haha

微信图片_20220908154224.png

相关文章
|
5天前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
143 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
26天前
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
28天前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
47 4
|
28天前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
52 1
|
2月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
1324 1
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
2月前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
181 26
|
28天前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
36 0
|
2月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
2月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
3月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
58 2

热门文章

最新文章