摘要:動(dòng)態(tài)代理的基本思想就是建立一個(gè)可以返回動(dòng)態(tài)代理對(duì)象的代理工廠,該代理工廠提供一個(gè)靜態(tài)方法,用戶自己的業(yè)務(wù)處理對(duì)象和系統(tǒng)服務(wù)對(duì)象傳遞給該代理工廠,在代理工廠的內(nèi)部,通過(guò)反射技術(shù),獲得該業(yè)務(wù)邏輯對(duì)象的類對(duì)象,從而自動(dòng)的拼裝一個(gè)業(yè)務(wù)邏輯類的子類,在該子類中,代理工廠自己完成我們業(yè)務(wù)邏輯和系統(tǒng)服務(wù)的動(dòng)態(tài)拼裝,然后代理工廠動(dòng)態(tài)編譯該類,動(dòng)態(tài)生成該代理類的對(duì)象,返回代理對(duì)象給客戶,從而實(shí)現(xiàn)了真正意義上的業(yè)務(wù)邏輯和系統(tǒng)服務(wù)邏輯之間的動(dòng)態(tài)組裝。
關(guān)鍵詞:動(dòng)態(tài)代理;反射;業(yè)務(wù)邏輯;系統(tǒng)服務(wù);業(yè)務(wù)接口;系統(tǒng)服務(wù)接口;動(dòng)態(tài)編譯
中圖分類號(hào):TP311文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2009)36-10427-03
Danymic Proxy Research and Implements
LIU Qing-jie, SUN Xu-guang, BAI Ling
(Science And Technology Department, Institute Of Disaster Prevention, Sanhe 065201, China)
Abstract: A dynamic proxy class is a class that implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface. Thus, a dynamic proxy class can be used to create a type-safe proxy object for a list of interfaces without requiring pre-generation of the proxy class, such as with compile-time tools. Method invocations on an instance of a dynamic proxy class are dispatched to a single method in the instance's invocation handler, and they are encoded with a java.lang reflect.Method object identifying the method that was invoked and an array of type Object containing the arguments.
Key words: dynamic proxy; reflect; business logic; business interface; system service Interface; dynamic compilation
我們知道,對(duì)于企業(yè)應(yīng)用開發(fā)來(lái)說(shuō),技術(shù)的核心就是通過(guò)DAO實(shí)現(xiàn)業(yè)務(wù)對(duì)象的增、刪、查、改,我們將這部分功能稱之為業(yè)務(wù)邏輯。但是,基于各種各樣的原因,我們?cè)谡鎸?shí)的業(yè)務(wù)邏輯代碼執(zhí)行之前,總是需要首先執(zhí)行一些系統(tǒng)級(jí)服務(wù):例如執(zhí)行時(shí)間測(cè)試、記錄日志、安全檢查、權(quán)限審核、事務(wù)等業(yè)務(wù)。傳統(tǒng)的做法是,直接將實(shí)現(xiàn)系統(tǒng)服務(wù)的代碼放置在我們的業(yè)務(wù)邏輯代碼之前。這樣開發(fā)帶來(lái)的問(wèn)題就是:一方面我們所有的業(yè)務(wù)邏輯代碼之前都放置大量的重復(fù)的系統(tǒng)服務(wù)代碼,造成一種虛假的代碼膨脹。另一方面我們的系統(tǒng)服務(wù)代碼和我們的業(yè)務(wù)邏輯代碼緊密耦合,非常的不利于應(yīng)用的擴(kuò)展、遷移和升級(jí)。解決之道就是——代理模式。
1 代理模式的解耦原理
代理模式分為靜態(tài)代理和動(dòng)態(tài)代理。[1]靜態(tài)代理的實(shí)現(xiàn)原理就是在使用簡(jiǎn)單的繼承方法,用一個(gè)新的類繼承我們的 DAOImpl類,然后再子類中將系統(tǒng)服務(wù)代碼加入,然后調(diào)用父類的業(yè)務(wù)邏輯代碼。這樣就把我們的系統(tǒng)服務(wù)代碼放置在子類,業(yè)務(wù)邏輯代碼放置在父類,實(shí)現(xiàn)簡(jiǎn)單解耦。一旦需要有變化,直接丟棄子類,然后再父類上的業(yè)務(wù)邏輯類上進(jìn)行二次開發(fā),并沒(méi)有帶來(lái)多少擴(kuò)展和升級(jí)上的方便。還有一種辦法就是動(dòng)態(tài)代理,也叫動(dòng)態(tài)植入,就是動(dòng)態(tài)生成業(yè)務(wù)邏輯類的子類,在子類中自動(dòng)將我們開發(fā)好的系統(tǒng)服務(wù)代碼植入到業(yè)務(wù)邏輯代碼之前。對(duì)于我們用戶來(lái)說(shuō),我們只要將我們的業(yè)務(wù)邏輯處理對(duì)象和系統(tǒng)服務(wù)對(duì)象傳遞給代理工廠,代理工廠自動(dòng)幫助我們生成一個(gè)代理對(duì)象,我們通過(guò)代理對(duì)象調(diào)用我們的業(yè)務(wù)邏輯。因?yàn)榇韺?duì)象是動(dòng)態(tài)生成,就實(shí)現(xiàn)了真正意義上的,我們的業(yè)務(wù)邏輯代碼和系統(tǒng)服務(wù)代碼的分離,各自的擴(kuò)展和升級(jí)不會(huì)對(duì)對(duì)方產(chǎn)生任何影響,實(shí)現(xiàn)真正意義上的解耦。
2 動(dòng)態(tài)代理的實(shí)現(xiàn)思想
要實(shí)現(xiàn)動(dòng)態(tài)代理,首先必須有一個(gè)統(tǒng)一的接口,讓我們的業(yè)務(wù)邏輯實(shí)現(xiàn)類該接口,這樣我們動(dòng)態(tài)生成的代理類和我們的業(yè)務(wù)邏輯類就有了統(tǒng)一訪問(wèn)接口;其次,為我們的系統(tǒng)服務(wù)類開發(fā)一個(gè)統(tǒng)一的接口,然后我們所有的系統(tǒng)服務(wù)類必須實(shí)現(xiàn)該接口。最后,開發(fā)我們代理工廠類,該代理工廠類代理,對(duì)外提供一個(gè)用來(lái)生成動(dòng)態(tài)代理對(duì)象的靜態(tài)方法,在該方法內(nèi)部,通過(guò)接收的業(yè)務(wù)對(duì)象和系統(tǒng)服務(wù)對(duì)象,動(dòng)態(tài)的構(gòu)建一個(gè)代理類源碼,然后動(dòng)態(tài)編譯該類,最后動(dòng)態(tài)生成一個(gè)代理對(duì)象,返回給客戶。
通過(guò)分析,我們知道,[2]實(shí)現(xiàn)一個(gè)動(dòng)態(tài)代理的關(guān)鍵是如果創(chuàng)建一個(gè)能夠自動(dòng)生成動(dòng)態(tài)代理對(duì)象的代理工廠。開發(fā)動(dòng)態(tài)代理工廠的第一關(guān)鍵是如果能夠生成動(dòng)態(tài)代理類的源代碼。本文使用反射技術(shù)獲得動(dòng)態(tài)業(yè)務(wù)接口和系統(tǒng)服務(wù)接口的內(nèi)部方法,自己手動(dòng)的拼裝出該動(dòng)態(tài)代理類的源代碼;開發(fā)代理工廠第二個(gè)關(guān)鍵是如果能夠在運(yùn)行期自動(dòng)將我們的動(dòng)態(tài)代理類源碼編譯成字節(jié)碼。JDK6.0提供了一個(gè)Compiler API,能夠是實(shí)現(xiàn)自動(dòng)編譯,本人采用這個(gè)辦法。
3 實(shí)現(xiàn)動(dòng)態(tài)代理工廠類
1) 首先編寫一個(gè)對(duì)外提供服務(wù)的靜態(tài)方法,該方法接收兩個(gè)參數(shù),一個(gè)是業(yè)務(wù)邏輯接口,一個(gè)是系統(tǒng)服務(wù)接口。
public static Object newDynanProxy(Class intf, SystemService service){}
2) 通過(guò)業(yè)務(wù)邏輯類對(duì)象,反射內(nèi)部方法,拼裝動(dòng)態(tài)代理類的內(nèi)部代碼
String str1= \"\";
String line = \"\\r\\";
//獲得業(yè)務(wù)邏輯類的所有方法對(duì)象
Method[] methods = intf.getMethods();
/*每次循環(huán),拼裝出一個(gè)動(dòng)態(tài)代理類的方法方法的內(nèi)部,調(diào)用我們的系統(tǒng)服務(wù)對(duì)象的接口方法,同時(shí)
將我們的業(yè)務(wù)邏輯方法對(duì)象傳遞到系統(tǒng)服務(wù)接口方法中*/
for(int i=0;i str1 += \"@Override\" + line+ \"public void \" + methods[i].getName() + \"() {\" + line+ \"try {\" + line+ \"Method md = \" + intf.getName() + \".class.getMethod(\\\"\" + methods[i].getName() + \"\\\");\" + line+ \"service.invoke(this, md);\" + line+ \"}catch(Exception ex) {}\" + line+ \"}\"; 3) 繼續(xù)拼裝動(dòng)態(tài)代理類源代碼,將我們的動(dòng)態(tài)代理類的方法封裝到一個(gè)動(dòng)態(tài)代理類中 Stringcode = \"package com.fzxy.proxy;\" +line+ \"importjava.lang.reflect.Method;\" + line+ \"public class $Proxy1 implements \" + intf.getName() + \"{\" + line+ \"public $Proxy1(SystemService service) {\" + line+ \"this.service = service;\" + line+ \"}\" + line+ \"com.fzxy.proxy.SystemServiceservice;\" + line+ str1+ \"}\"; 4) 創(chuàng)建一個(gè)臨時(shí)文件,將該動(dòng)態(tài)代理類源代碼輸入到該臨時(shí)文件 String file = System.getPropeline y(\"user.dir\")+\"/com/bjsxt/proxy/$Proxy1.java\"; File f = new File(fileName); FileWriter fw = new FileWriter(f); fw.write(code); fw.flush(); fw.close(); 5) 使用JDK6.0 Comiler動(dòng)態(tài)編譯該源代碼文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileMgr = compiler.getStandardFileManager(1, 1, 1); Iterable units = fileMgr.getJavaFileObjects(fileName); CompilationTask t = compiler.getTask(1, fileMgr, 1, 1, 1, units); t.call(); fileMgr.close(); 6) 動(dòng)態(tài)裝載該動(dòng)態(tài)代理類,創(chuàng)建一個(gè)代理對(duì)象對(duì)象,返回該對(duì)象 URL[] urls = new URL[] {new URL(\"file:/\" + fileName)}; URLClassLoader ul = new URLClassLoader(urls); Class c = ul.loadClass(\"com.fzxy.proxy.$Proxy1\"); Constructor ctr = c.getConstructor(SystemService.class); Object m = ctr.newInstance(service); return m; 4 系統(tǒng)服務(wù)接口的實(shí)現(xiàn) 所有類型的系統(tǒng)服務(wù)類必須實(shí)現(xiàn)該接口,這樣,我們的代理工廠就可以通過(guò)約定的接口方法,在生成的動(dòng)態(tài)代理類方法中調(diào)用該接口方法,將調(diào)用真是的調(diào)用傳遞到該系統(tǒng)服務(wù)對(duì)象的接口方法中。 package com.fzxy.proxy; public interface SystemService{ public void invoke(Object o, Method m); } 5 應(yīng)用舉例 我們實(shí)現(xiàn)一個(gè)訂單處理類,該類提供訂單的增刪改查業(yè)務(wù);現(xiàn)在我們希望能夠測(cè)試訂單增刪改查的執(zhí)行時(shí)間,因?yàn)樵摴ぷ鞑粚儆谟唵翁幚順I(yè)務(wù)必須的工作,所以我們不希望執(zhí)行時(shí)間測(cè)試的代碼和我們的訂單業(yè)務(wù)處理緊耦合。[3]按照動(dòng)態(tài)代理的思想,我們可以講執(zhí)行時(shí)間測(cè)試封裝為一個(gè)系統(tǒng)服務(wù)類,通過(guò)我們的動(dòng)態(tài)代理工廠,為我們生成一個(gè)能夠測(cè)試訂單業(yè)務(wù)執(zhí)行時(shí)間的動(dòng)態(tài)代理對(duì)象,通過(guò)該對(duì)象,我們就可以獲得一個(gè)能夠測(cè)試執(zhí)行時(shí)間的訂單處理對(duì)象的時(shí)間代理對(duì)象。 1) 開發(fā)一個(gè)時(shí)間測(cè)試系統(tǒng)服務(wù)類,該類實(shí)現(xiàn)系統(tǒng)服務(wù)接口 package com.fzxy.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TimeSystemService implements SystemService {private Object target; public TimeHandler(Object target) {this.target = target; }@Override public void invoke(Object o, Method m) {long start = System.currentTimeMillis(); try {m.invoke(target); } catch (Exception e) {e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println(\"執(zhí)行時(shí)間:\" + (end-start)+\"毫秒\"); }} 2) 開發(fā)一個(gè)訂單業(yè)務(wù)處理接口 package com.fzxy.service; public interface OrderManager {void addOrder(Order order); void findOrder(int id); void updateOrder(Order order); void deleteOrder(int id);} 3) 開一個(gè)訂單業(yè)務(wù)處理類 package com.fzxy.service; public interface OrderManagerImpl {public void findOrder(int id) {//查找的業(yè)務(wù)代碼 } public void addOrder(Order order) {//增加的業(yè)務(wù)代碼 } public void updateOrder(Order order) {//更新的業(yè)務(wù)代碼 } public void deleteOrder(int id) {//刪除的邏輯代碼 }} 4) 使用動(dòng)態(tài)代理工廠實(shí)現(xiàn)訂單業(yè)務(wù)邏輯和執(zhí)行時(shí)間測(cè)試的系統(tǒng)服務(wù)邏輯的動(dòng)態(tài)拼裝。 OrderManager mgr = new OrderManagerImpl(); SystemServiceservice = new TimeSystemService(mgr); OrderManagermgrproxy= (OrderManager)Proxy.newProxyInstance(UserMgr.class,h); //下面代碼是通過(guò)代理對(duì)象實(shí)現(xiàn) mgrproxy.addOrder(new Order()); mgrproxy.updateOrder(new Order()); mgrproxy.deleteOrder(1); mgrproxy.findOrder(2); 6 結(jié)束語(yǔ) 動(dòng)態(tài)代理在各種中間件中大量應(yīng)用,例如:Spring的AOP、連接池、RMI、等。動(dòng)態(tài)代理能夠?qū)崿F(xiàn)真正意義上的不相關(guān)業(yè)務(wù)的動(dòng)態(tài)組裝,對(duì)于不同業(yè)務(wù)的人員,只需要專注自己業(yè)務(wù)的開發(fā),不需要知道其它業(yè)邏輯的存在。動(dòng)態(tài)代理一面可以很好的實(shí)現(xiàn)軟件不同業(yè)務(wù)邏輯的解耦,還可以減少軟件開發(fā)人員掌握不同業(yè)務(wù)邏輯代理的邏輯爆炸的壓力,對(duì)于提交軟件質(zhì)量,輕松實(shí)現(xiàn)軟件的擴(kuò)展和升級(jí)都有非常重要的現(xiàn)實(shí)意義。 參考文獻(xiàn): [1] 閻宏.Java與設(shè)計(jì)模式[M].北京:郵電出版社,2008. [2] 透明.代理模式的前世今生[M].北京:清華大學(xué)出版社,2008. [3] Hou F.Dynamic Proxy 在Java RMI中的應(yīng)用[M].北京:清華大學(xué)出版社,2009.