建設(shè)網(wǎng)工程信息上??焖賰?yōu)化排名
?????最近在學習lua,然后順便看了下luaj,可能用的人比較少,網(wǎng)上關(guān)于luaj的文章較少,其中在網(wǎng)上找到這個博主的相關(guān)文章,很詳細,對于要學習luaj的小伙伴可以兩篇一起查看,本文在此基礎(chǔ)上進行擴展。
?????本文的luaj版本是:luaj-3.0.1
珠玉在前》》》》Luaj學習筆記(二) - 在Lua中操作Java對象
LuaJ源碼中org.luaj.vm2.lib.jse.LuajavaLib,是我們在lua中操作java的主要方法庫定義。
luajava有五種方法讓我們操作java類:bindClas
、 newInstance
、 new
、 createProxy
、 loadLib
先放上源碼,然后我們一步步解析,bindClas
、 newInstance
、 new
使用方案和案例在前一篇文章就已經(jīng)解釋過了,這里不做過多贅述,對于前篇不詳細的createProxy
和loadLib
做重點介紹:
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 constructorfinal 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 interfacesfinal Class[] ifaces = new Class[niface];for ( int i=0; i<niface; i++ ) ifaces[i] = classForName(args.checkjstring(i+1));// create the invocation handlerInvocationHandler handler = new ProxyInvocationHandler(lobj);// create the proxy objectObject proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler);// return the proxyreturn LuaValue.userdataOf( proxy );}case LOADLIB: {// get constructorString 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的類,該類是對我們參數(shù)指定的java類的一個包裝。bindClass方法返回類實例class,同時可以調(diào)用類實例的new方法生成該class的實例object,然后就可以調(diào)用實例方法(實例化的調(diào)用這一點和學lua時的面向?qū)ο蟮膶懛ê芟嗨?#xff09;
該方法適合調(diào)用java類的
靜態(tài)方法
和靜態(tài)屬性
,當然也可以用來實例化對象,但newInstance 和 new更符合語義
源碼:
case BINDCLASS: {final Class clazz = classForName(args.checkjstring(1));return JavaClass.forClass(clazz);}JavaClass:static 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("靜態(tài)方法調(diào)用:",classx:method004())
print("靜態(tài)屬性值:",classx.s4)local obj = classx:new()
print("實例方法調(diào)用:",obj:method001())
print("實例屬性值:",obj.s1)
輸出結(jié)果:
靜態(tài)方法調(diào)用: method004
靜態(tài)屬性值: s4
實例方法調(diào)用: method001
實例方法調(diào)用: s1
其中,我們在lua腳本中用類實例調(diào)用:new()
方法時,實際是invoke JavaClass的new方法,而這個new方法通過getMethod方法拿到的是他的構(gòu)造器方法(在下一個方法源碼中)
newInstance || new
通過源碼可以看到 newInstance 和 new方法的大部分邏輯是一致的
入?yún)⑹怯袇^(qū)別的:
- newInstance 方法,接受一個字符串的類路徑然后轉(zhuǎn)換為Class實例;
- new方法接受一個LuaValue對象,從中拿出userdata數(shù)據(jù),而這個userdata數(shù)據(jù)是一個Class實例。
而上一個方法bindClass返回的就是一個包裝了Class的LuaValue對象,因此我們可知,在使用new方法的時候,需要先用bindClass方法生成一個Class實例出來,而newInstance就不用了
??????然后代碼對我們在lua中傳入的參數(shù),截取后邊的一段用作構(gòu)造器參數(shù),然后調(diào)用我們在上一個方法看到JavaClass.forClass包裝Class實例,然后獲取該類的構(gòu)造器,然后invoke調(diào)用構(gòu)造器方法,生成對象。
源碼:
case NEWINSTANCE:case NEW: {// get constructorfinal 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);}JavaClass:public 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("實例方法調(diào)用:",obj:method001())
print("實例屬性值:",obj.s1)
print("-----------")local obj2 = luajava.newInstance(className)
print("newInstance>實例方法調(diào)用:",obj2:method001())
print("newInstance>實例屬性值:",obj2.s1)
輸出結(jié)果:
new>實例方法調(diào)用: method001
new>實例屬性值: s1
-----------
newInstance>實例方法調(diào)用: method001
newInstance>實例屬性值: s1
createProxy
???????createProxy可以像JDKProxy那樣的在lua中創(chuàng)建對一個對象的一個或多個方法的代理
???????事實上,createProxy使用的代理就是JDKProxy,只不過在用法上和我們通常的用法有些許變動,在最終的方法的invoke時,并不是通常的使用JAVA反射中的的Method的invoke,而是Luaj自己LuaValue的invoke,也就是說通過調(diào)用在lua腳本中定義的那個擴展方法,去實現(xiàn)代理,實際上的代理邏輯都在lua中定義(下面lua腳本可以看出來)
???????還有很重要的一點,在lua中通過createproxy生成出來的代理對象,是不能在lua腳本中去直接調(diào)用代理對象的方法的,?????只能通過將該代理對象當做參數(shù)通過調(diào)用類方法或者對象方法之后在java中去觸發(fā)代理對象的方法。(看下邊的測試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 interfacesfinal Class[] ifaces = new Class[niface];for ( int i=0; i<niface; i++ ) ifaces[i] = classForName(args.checkjstring(i+1));// create the invocation handlerInvocationHandler handler = new ProxyInvocationHandler(lobj);// create the proxy objectObject proxy = Proxy.newProxyInstance(getClass().getClassLoader(), ifaces, handler);// return the proxyreturn LuaValue.userdataOf( proxy );}
測試java代碼:
public interface Car {void running(String carName);void blow(Integer num);}
-------------------------------------------------
public class Taxi implements Car {@Overridepublic void running(String carName) {System.out.println("The taxi["+carName+"] is running.");}@Overridepublic 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)-- InvocationHandlerlocal 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代碼中去調(diào)用代理對象的方法luajava.bindClass(className):triggerProxy(proxyObj)
輸出結(jié)果:
這是對Car的代理邏輯----前
The taxi[74f253d1-c0d8-47ea-9799-01401174abe4] is running.
這是對Car的代理邏輯----后
--------
Car--blow----前
didi
Car--blow----后
loadLib
關(guān)于loadLib
這個方法,前邊文章可能由于歷史更新的原因,在luaj-3.0.1版本中代碼是不對的
?????loadLib通過源碼查看我,我們其實可以看到,他只用兩個參數(shù),第一個是類路徑,第二個是靜態(tài)方法名,這個方法要么返回一個LuaValue類型的結(jié)果,要么不返回或者其他返回類型都會被loadLib轉(zhuǎn)為nil
。
源碼:
case LOADLIB: {// get constructorString 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執(zhí)行啦!\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)
輸出結(jié)果:
xxx執(zhí)行啦!
196 nil
總結(jié)
bindClass
適合做類的靜態(tài)方法和靜態(tài)屬性的取值的操作,也是使用new
方法的前置操作。
newInstance
和new
適合用作java對象實例化之后對實例對象的操作和取值
createProxy
適合用作JDKProxy的替代用法,需要注意的是在被代理對象和代理對象都在腳本中生成,且代理對象不能直接在lua中去調(diào)用代理方法執(zhí)行,需要以傳參的形式給到j(luò)ava方法調(diào)用java方法觸發(fā)。
loadLib
用于類的無參靜態(tài)方法的調(diào)用,如果需要返回值,則需要定義方法返回類型為LuaValue