Java---基于Nashorn的動(dòng)態(tài)代理類(lèi)(非接口)

動(dòng)態(tài)代理(Dynamic Proxy)是一種在運(yùn)行時(shí)生成代理對(duì)象的技術(shù),用于攔截并修改方法調(diào)用或添加額外的邏輯。
面試官:為什么動(dòng)態(tài)代理只能代理接口?
你:為什么不能?
面試官:????
搭嘎,動(dòng)態(tài)代理機(jī)制限制了只能代理接口。這是因?yàn)镴ava的動(dòng)態(tài)代理是基于接口的代理,它的實(shí)現(xiàn)方式是在運(yùn)行時(shí)生成一個(gè)代理類(lèi),該代理類(lèi)實(shí)現(xiàn)了目標(biāo)接口,并且可以攔截該接口中的方法調(diào)用。那么有沒(méi)有辦法實(shí)現(xiàn)動(dòng)態(tài)代理類(lèi)呢?這并不是不可能,借助Nashorn這一腳本引擎的
Java.extend我們可以做到這一點(diǎn)。

Nashorn
先來(lái)看一下Oracle官網(wǎng)對(duì)Nashorn類(lèi)繼承的描述:http://www.oracle.com/pls/topic/lookup?ctx=E92405-01&id=JSJSG-GUID-B631F440-2320-47D7-8012-85A70A40F986

可以看到代碼中MyRun繼承了Runnable類(lèi)并實(shí)現(xiàn)了run方法
Java.extend的第一個(gè)參數(shù)是一個(gè)Nashorn里面的NativeJavaType
第二個(gè)參數(shù)是JavaScript里面的Object(非java.lang.Object),里面的屬性名稱(chēng)代表著要實(shí)現(xiàn)的方法(父類(lèi)擁有的方法),后面接著一個(gè)function方法體。
那么依樣畫(huà)個(gè)葫蘆。
我們寫(xiě)一個(gè)根據(jù)Class的名稱(chēng)來(lái)繼承對(duì)應(yīng)類(lèi)并實(shí)現(xiàn)對(duì)應(yīng)方法的功能。

"target.getClass().static"將一個(gè)java.lang.Class對(duì)象轉(zhuǎn)換成了Nashorn的NativeJavaType,以便可以通過(guò)Java.extend繼承。
"object"是一個(gè)JavaScript的Object對(duì)象,代理類(lèi)方法
其中Java.to(arguments,"java.lang.Object[]")
將方法的參數(shù)傳入了InvocationHandler.invoke
至于arguments,詳見(jiàn)https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments
bang.Utils.convertFatherToSon是我封裝的一個(gè)將父類(lèi)轉(zhuǎn)為子類(lèi)的Java方法,將target轉(zhuǎn)成了我們代理的新的類(lèi),示例見(jiàn)我的另一篇文章。
那么我就在Nashorn里面實(shí)現(xiàn)了這個(gè)功能,那怎么將它應(yīng)用到Java當(dāng)中,并且不要損失太大效率呢?

Java
對(duì)上述Nashorn代碼封裝一下。

Nashorn代碼的最后將一個(gè)InvocationHandler數(shù)組傳到了Java里面
并將一個(gè)InvocationHandler賦值給了一個(gè)全局變量proxyHandler,方便后來(lái)調(diào)用。

因此可以寫(xiě)出這一方法,類(lèi)似于原動(dòng)態(tài)代理接口。
InvocationHandler2是我繼承InvocationHandler的一個(gè)子類(lèi)

它的invoke方法如上,其他方法省略。
那么我們就可以在Java里面動(dòng)態(tài)代理類(lèi)了。
面試官:我問(wèn)的是這個(gè)嗎?

上述的完整代碼(包括其他方法)已在Github里面:
https://github.com/sdfkl2890/SpecialTools
具體請(qǐng)看其中的bang.Utils類(lèi),bang.Test類(lèi)則是示例類(lèi)