LuaJava操作Java的方法

news2024/11/15 17:37:10

     最近在学习lua,然后顺便看了下luaj,可能用的人比较少,网上关于luaj的文章较少,其中在网上找到这个博主的相关文章,很详细,对于要学习luaj的小伙伴可以两篇一起查看,本文在此基础上进行扩展。
     本文的luaj版本是:luaj-3.0.1

珠玉在前》》》》Luaj学习笔记(二) - 在Lua中操作Java对象

LuaJ源码中org.luaj.vm2.lib.jse.LuajavaLib,是我们在lua中操作java的主要方法库定义。
在这里插入图片描述

luajava有五种方法让我们操作java类:bindClasnewInstancenewcreateProxyloadLib

先放上源码,然后我们一步步解析,bindClasnewInstancenew 使用方案和案例在前一篇文章就已经解释过了,这里不做过多赘述,对于前篇不详细的createProxyloadLib做重点介绍:

public Varargs invoke(Varargs args) {
		try {
			switch ( opcode ) {
			case INIT: {
				// LuaValue modname = args.arg1();
				LuaValue env = args.arg(2);
				LuaTable t = new LuaTable();
				bind( t, this.getClass(), NAMES, BINDCLASS );
				env.set("luajava", t);
				env.get("package").get("loaded").set("luajava", t);
				return t;
			}
			case BINDCLASS: {
				final Class clazz = classForName(args.checkjstring(1));
				return JavaClass.forClass(clazz);
			}
			case NEWINSTANCE:
			case NEW: {
				// get constructor
				final LuaValue c = args.checkvalue(1); 
				final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class));
				final Varargs consargs = args.subargs(2);
				return JavaClass.forClass(clazz).getConstructor().invoke(consargs);
			}
				
			case CREATEPROXY: {				
				final int niface = args.narg()-1;
				if ( niface <= 0 )
					throw new LuaError("no interfaces");
				final LuaValue lobj = args.checktable(niface+1);
				
				// get the interfaces
				final Class[] ifaces = new Class[niface];
				for ( int i=0; i<niface; i++ ) 
					ifaces[i] = classForName(args.checkjstring(i+1));
				
				// create the invocation handler
				InvocationHandler handler = new ProxyInvocationHandler(lobj);
				
				// create the proxy object
				Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler);
				
				// return the proxy
				return LuaValue.userdataOf( proxy );
			}
			case LOADLIB: {
				// get constructor
				String classname = args.checkjstring(1);
				String methodname = args.checkjstring(2);
				Class clazz = classForName(classname);
				Method method = clazz.getMethod(methodname, new Class[] {});
				Object result = method.invoke(clazz, new Object[] {});
				if ( result instanceof LuaValue ) {
					return (LuaValue) result;
				} else {
					return NIL;
				}
			}
			default:
				throw new LuaError("not yet supported: "+this);
			}
		} catch (LuaError e) {
			throw e;
		} catch (InvocationTargetException ite) {
			throw new LuaError(ite.getTargetException());
		} catch (Exception e) {
			throw new LuaError(e);
		}
	}

bindclass

bindclass:返回一个JavaClass的类,该类是对我们参数指定的java类的一个包装。bindClass方法返回类实例class,同时可以调用类实例的new方法生成该class的实例object,然后就可以调用实例方法(实例化的调用这一点和学lua时的面向对象的写法很相似)

该方法适合调用java类的静态方法静态属性,当然也可以用来实例化对象,但newInstance 和 new更符合语义

源码:

			case BINDCLASS: {
				final Class clazz = classForName(args.checkjstring(1));
				return JavaClass.forClass(clazz);
			}

JavaClassstatic final LuaValue NEW = valueOf("new");
    
    static JavaClass forClass(Class var0) {
        JavaClass var1 = (JavaClass)classes.get(var0);
        if (var1 == null) {
            classes.put(var0, var1 = new JavaClass(var0));
        }

        return var1;
    }
	

    public LuaValue getConstructor() {
        return this.getMethod(NEW);
    }

测试java代码:

public class TestClass {
    public String s1;
    protected String s2;
    private String s3;
    public static String s4;
    
    public String method001(){        return "method001";    }
    protected void method002(){    }
    private void method003(){    }
    public static String method004(){        return "method004";    }
}

lua脚本:

local className = "com.test.luaj.TestClass"

local classx = luajava.bindClass(className)
print("静态方法调用:",classx:method004())
print("静态属性值:",classx.s4)

local obj = classx:new()
print("实例方法调用:",obj:method001())
print("实例属性值:",obj.s1)

输出结果:

静态方法调用:	method004
静态属性值:	s4
实例方法调用:	method001
实例方法调用:	s1

其中,我们在lua脚本中用类实例调用:new()方法时,实际是invoke JavaClass的new方法,而这个new方法通过getMethod方法拿到的是他的构造器方法(在下一个方法源码中)

newInstance || new

通过源码可以看到 newInstance 和 new方法的大部分逻辑是一致的

入参是有区别的:

  1. newInstance 方法,接受一个字符串的类路径然后转换为Class实例;
  2. new方法接受一个LuaValue对象,从中拿出userdata数据,而这个userdata数据是一个Class实例
    而上一个方法bindClass返回的就是一个包装了Class的LuaValue对象,因此我们可知,在使用new方法的时候,需要先用bindClass方法生成一个Class实例出来,而newInstance就不用了

      然后代码对我们在lua中传入的参数,截取后边的一段用作构造器参数,然后调用我们在上一个方法看到JavaClass.forClass包装Class实例,然后获取该类的构造器,然后invoke调用构造器方法,生成对象

源码:

			case NEWINSTANCE:
			case NEW: {
				// get constructor
				final LuaValue c = args.checkvalue(1); 
				final Class clazz = (opcode==NEWINSTANCE? classForName(c.tojstring()): (Class) c.checkuserdata(Class.class));
				final Varargs consargs = args.subargs(2);
				return JavaClass.forClass(clazz).getConstructor().invoke(consargs);
			}

JavaClasspublic LuaValue getConstructor() {
		return getMethod(NEW);
	}

LuaValue getMethod(LuaValue key) {
···
			Map map = new HashMap();
			Constructor[] c = ((Class)m_instance).getConstructors();
			List list = new ArrayList();
			for ( int i=0; i<c.length; i++ ) 
				if ( Modifier.isPublic(c[i].getModifiers()) )
					list.add( JavaConstructor.forConstructor(c[i]) );
			switch ( list.size() ) {
			case 0: break;
			case 1: map.put(NEW, list.get(0)); break;
			default: map.put(NEW, JavaConstructor.forConstructors( (JavaConstructor[])list.toArray(new JavaConstructor[list.size()]) ) ); break;
			}
···
}

测试代码:

local className = "com.test.luaj.TestClass"

local classx = luajava.bindClass(className)
local obj = luajava.new(classx)
print("实例方法调用:",obj:method001())
print("实例属性值:",obj.s1)
print("-----------")

local obj2 = luajava.newInstance(className)
print("newInstance>实例方法调用:",obj2:method001())
print("newInstance>实例属性值:",obj2.s1)

输出结果:

new>实例方法调用:	method001
new>实例属性值:	s1
-----------
newInstance>实例方法调用:	method001
newInstance>实例属性值:	s1

createProxy

       createProxy可以像JDKProxy那样的在lua中创建对一个对象的一个或多个方法的代理
       事实上,createProxy使用的代理就是JDKProxy,只不过在用法上和我们通常的用法有些许变动,在最终的方法的invoke时,并不是通常的使用JAVA反射中的的Method的invoke,而是Luaj自己LuaValue的invoke,也就是说通过调用在lua脚本中定义的那个扩展方法,去实现代理,实际上的代理逻辑都在lua中定义(下面lua脚本可以看出来)
       还有很重要的一点,在lua中通过createproxy生成出来的代理对象,是不能在lua脚本中去直接调用代理对象的方法的,?????只能通过将该代理对象当做参数通过调用类方法或者对象方法之后在java中去触发代理对象的方法。(看下边的测试lua源码)

源码:

			case CREATEPROXY: {				
				final int niface = args.narg()-1;
				if ( niface <= 0 )
					throw new LuaError("no interfaces");
				final LuaValue lobj = args.checktable(niface+1);
				
				// get the interfaces
				final Class[] ifaces = new Class[niface];
				for ( int i=0; i<niface; i++ ) 
					ifaces[i] = classForName(args.checkjstring(i+1));
				
				// create the invocation handler
				InvocationHandler handler = new ProxyInvocationHandler(lobj);
				
				// create the proxy object
				Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler);
				
				// return the proxy
				return LuaValue.userdataOf( proxy );
			}


在这里插入图片描述

测试java代码:

public interface Car {
        void running(String carName);
        void blow(Integer num);
    }
-------------------------------------------------
public  class Taxi implements Car {
        @Override
        public void running(String carName) {
            System.out.println("The taxi["+carName+"] is running.");
        }

    @Override
    public void blow(Integer num) {
        for (int i = 0; i < num; i++) {
            System.out.print("didi\n");
        }
        System.out.print("");
    }


    public static void triggerProxy(Car car){
            String carName = UUID.randomUUID().toString();
            car.running(carName);
            System.out.println("--------");
            car.blow((int)Math.random()*10+1);
        }
    }
-----------------------------------------------------

public class LuaJCreateProxyTest {
    public static void main(String[] args) throws ScriptException {
        createProxy002();
    }
    static void createProxy002() throws ScriptException {
        Globals globals = JsePlatform.standardGlobals();
        globals.loadfile("res/lua/createProxyTest02.lua").call();
    }
}

lua脚本:

    interfaceName = "com.test.luaj.Car"
    className = "com.test.luaj.Taxi"

    taxi = luajava.newInstance(className)

   -- InvocationHandler
    local exit_cb = {
        running = function (carname)
            print("这是对Car的代理逻辑----前")
            taxi:running(carname)
            print("这是对Car的代理逻辑----后")
        end,
        blow = function(num)
        print("Car--blow----前")
            taxi:blow(num)
        print("Car--blow----后")
        end

    }

    proxyObj = luajava.createProxy(interfaceName ,exit_cb)

    -- proxyObj.running()   -- 会报错,不能这样使用
    --必须通过传参给java方法,在java代码中去调用代理对象的方法
    luajava.bindClass(className):triggerProxy(proxyObj)

输出结果:

这是对Car的代理逻辑----前
The taxi[74f253d1-c0d8-47ea-9799-01401174abe4] is running.
这是对Car的代理逻辑----后
--------
Car--blow----前
didi
Car--blow----后

loadLib

关于loadLib这个方法,前边文章可能由于历史更新的原因,在luaj-3.0.1版本中代码是不对的
     loadLib通过源码查看我,我们其实可以看到,他只用两个参数,第一个是类路径,第二个是静态方法名,这个方法要么返回一个LuaValue类型的结果,要么不返回或者其他返回类型都会被loadLib转为nil

源码:

case LOADLIB: {
				// get constructor
				String classname = args.checkjstring(1);
				String methodname = args.checkjstring(2);
				Class clazz = classForName(classname);
				Method method = clazz.getMethod(methodname, new Class[] {});
				Object result = method.invoke(clazz, new Object[] {});
				if ( result instanceof LuaValue ) {
					return (LuaValue) result;
				} else {
					return NIL;
				}
			}

测试java源码:

    public static LuaInteger xxx() throws ScriptException {
        System.out.println("xxx执行啦!\t"+(int)Math.pow(14,2));
        return LuaValue.valueOf((int)Math.pow(14,2));

    }

    static void test007() throws ScriptException {
        Globals globals = JsePlatform.standardGlobals();
        globals.loadfile("res/lua/loadLibTest.lua").call();

    }

lua脚本

local className = "com.yangsong.luaj.LuaJTest"
local method = 'xxx'
local _, result = luajava.loadLib(className, method)
print(_,result)

输出结果:

xxx执行啦!	
196	nil

总结

bindClass适合做类的静态方法和静态属性的取值的操作,也是使用new方法的前置操作。
newInstancenew适合用作java对象实例化之后对实例对象的操作和取值
createProxy适合用作JDKProxy的替代用法,需要注意的是在被代理对象和代理对象都在脚本中生成,且代理对象不能直接在lua中去调用代理方法执行,需要以传参的形式给到java方法调用java方法触发。
loadLib用于类的无参静态方法的调用,如果需要返回值,则需要定义方法返回类型为LuaValue

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1325052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DDPM详解

DDPM详解 参考 https://www.bilibili.com/video/BV1pa411u7G3/ 系列 DDPM 可以分为 Diffusion 和 Reverse 两个阶段。其中 Diffusion 阶段通过不断地对真实图片添加噪声&#xff0c;最终得到一张噪声图片。而 Reverse 阶段&#xff0c;模型需要学习预测出一张噪声图片中的噪声部…

LLM 和搜索引擎是一样的吗?

在这篇文章中&#xff0c;了解更多关于 AI 大型语言模型&#xff08;如 ChatGPT&#xff09;的潜力。了解他们如何彻底改变生产力&#xff0c;并探索他们与搜索引擎不断变化的关系。 像 ChatGPT 这样的 AI 大型语言模型 &#xff08;LLM&#xff09; 已经风靡全球&#xff0c;并…

【力扣】148.排序链表

148.排序链表 怎么说&#xff0c;这道题看上去挺简单的&#xff0c;但是要搞清楚的知识点那还真不少&#xff0c;刷题好痛苦&#xff0c;但是要刷&#xff01;嘿嘿~ 首先&#xff0c;要搞懂归并排序&#xff0c;然后是递归。这道题我刚开始想的是递归&#xff0c;但是题友说时…

macOS制作dmg包

macOS制作dmg包 准备&#xff1a;磁盘工具、以及要制作的软件&#xff0c;这里以Firefox为例 图片素材 背景图&#xff1a; 找到Firefox&#xff0c;点击显示简介&#xff0c;查看包的大小 打开磁盘工具 文件–>新建映像–>空白映像 填写信息&#xff0c;大小…

vue的语法模板与数据绑定的说明

vue的两大模板语法&#xff1a; 1.插值语法 2.指定语法 插值语法&#xff1a;{{}} 功能&#xff1a;用于解析标签体的内容 写法&#xff1a;{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性 指定语法&#xff1a; 功能:用于解析标签(包括:标签属性、标…

w13渗透测试实战之https账号密码捕抓

此次实验需要配合arp欺骗,不知道arp欺骗的&#xff0c;可以查看这篇w13渗透测试实战之ARP欺骗攻击&#xff08;ARP断网攻击&#xff09; 一、实验环境 攻击机&#xff1a;linux Kali 靶机&#xff1a;window 7 网络环境&#xff1a;虚拟机NAT 网关&#xff1a;192.168.89.2实…

百度侯震宇:AI原生与大模型将从三个层面重构云计算

12月20日&#xff0c;2023百度云智大会智算大会在北京举办&#xff0c;大会以「大模型重构云计算&#xff0c;Cloud for AI」为主题&#xff0c;深度聚焦大模型引发的云计算变革。 百度智能云表示&#xff0c;为满足大模型落地需求&#xff0c;正在基于「云智一体」战略重构…

Xcode 恢复Discard Changes

当开发的时候&#xff0c;Discard All Changes后 文件的修改都被放弃了&#xff0c;怎么才可以撤销更改呢 Xcode和Git没有这个功能&#xff0c;Finder可以实现 首先我们先退出Xcode用TextEdit打开你想恢复的文件转到文件 > 还原到 > 浏览所有版本...选择你想恢复的版本即…

亚信安慧AntDB:支撑中国广电5G业务的数据库之力

自2019年6月获得5G牌照以来&#xff0c;中国广电积极利用700MHz频谱资源&#xff0c;迅速崛起为第四大运营商&#xff0c;标志着其在数字通信领域取得的巨大成就。通过与中国移动紧密合作&#xff0c;共建共享基站已超过400万座&#xff0c;为实现自主运营和差异化竞争提供了坚…

vue2 组件传递数据

向子组件传递数据通过Props 1.创建子组件 详细步骤&#xff1a; 1.在components创建子组件 2.等父组件接受到参数后通过Props来接受父组件传递过来的数据 <template><div id"app"><h2>title:{{ title }}</h2><p>tips:{{ tips }}<…

java并发编程五 ReentrantLock,锁的活跃性

多把锁 一间大屋子有两个功能&#xff1a;睡觉、学习&#xff0c;互不相干。 现在小南要学习&#xff0c;小女要睡觉&#xff0c;但如果只用一间屋子&#xff08;一个对象锁&#xff09;的话&#xff0c;那么并发度很低 解决方法是准备多个房间&#xff08;多个对象锁&#xf…

idea中使用wsl作为启动项目的环境,便于linux环境下的测试

在idea中使用wsl作为启动项目的系统环境&#xff0c;首先安装wsl&#xff0c;这里不做过多的介绍&#xff0c;可以直接去微软官网看教程&#xff0c;也可以自己搜教程。 在wsl中安装jdk&#xff0c;linux中直接用命令安装openjdk即可 sudo apt-get updatesudo apt install ope…

ios微信小程序table头部与左侧固定双重滚动会抖动的坑,解决思路

正常情况是左右滑动时&#xff0c;左侧固定不动&#xff0c;上下滑动时表头不动&#xff1b;而且需求不是完整页面滚动。而是单独这个表滚动&#xff1b; 第一个坑是他有一个ios自带的橡胶上下回弹效果。导致滚动时整个表都跟着回弹&#xff1b; 这个是很好解决。微信开发官网…

在使用mapstruct,想忽略掉List<DTO>字段里面的,`data` 字段的映射, 如何写ignore: 使用@IterableMapping

在使用mapstruct,想忽略掉List字段里面的,data 字段的映射, 如何写ignore 代码如下: public interface AssigmentFileMapper {AssigmentFileDTO assigmentFileToAssigmentFileDTO(AssigmentFile assigmentFile);AssigmentFile assigmentFileDTOToAssigmentFile(Assigment…

Notepad++:多行数据操作

1&#xff09;删除关键字之后&#xff08;或之前&#xff09;的所有字符 删除s之后&#xff08;包含s&#xff09;的所有内容&#xff1b;快捷键&#xff1a;s.*$ 替换成功 删除s之前&#xff08;包含s&#xff09;的所有内容&#xff1b;快捷键&#xff1a;^.*s 2&#xff09…

MAC苹果笔记本电脑如何彻底清理垃圾文件软件?

苹果电脑以其流畅的操作系统和卓越的性能而备受用户喜爱。然而&#xff0c;随着时间的推移&#xff0c;系统可能会积累大量垃圾文件&#xff0c;影响性能。本文将介绍苹果电脑怎么清理垃圾文件的各种方法&#xff0c;以提升系统运行效率。 CleanMyMac X是一款专业的Mac清理软件…

语音识别之百度语音试用和OpenAiGPT开源Whisper使用

0.前言: 本文作者亲自使用了百度云语音识别,腾讯云,java的SpeechRecognition语言识别包 和OpenAI近期免费开源的语言识别Whisper(真香警告)介绍了常见的语言识别实现原理 1.NLP 自然语言处理(人类语言处理) 你好不同人说出来是不同的信号表示 单位k 16k16000个数字表示 1秒160…

智能优化算法应用:基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于水基湍流算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.水基湍流算法4.实验参数设定5.算法结果6.…

Super访问父类成员

1 问题 当子类的成员变量或方法与父类同名时&#xff0c;可能模糊不清&#xff0c;应该怎么解决&#xff1f;如果子类重写了父类的某一个方法&#xff0c;我们又该怎么调用父类的方法&#xff1f; 2 方法 super调用成员属性&#xff1a; 当父类和子类具有相同的数据成员时&…

面向 NLP 任务的大模型 Prompt 设计

很久之前&#xff0c;我们介绍到&#xff0c;prompt是影响下游任务的关键所在&#xff0c;当我们在应用chatgpt进行nlp任务落地时&#xff0c;如何选择合适的prompt&#xff0c;对于SFT以及推理环节尤为重要。 不过&#xff0c;硬想不是办法&#xff0c;我们可以充分参考开源的…