代理實(shí)現(xiàn)可以分為靜態(tài)代理和動(dòng)態(tài)代理。
靜態(tài)代理模式其實(shí)很常見(jiàn),比如買(mǎi)火車(chē)票這件小事:黃牛相當(dāng)于是火車(chē)站的代理,我們可以通過(guò)黃牛買(mǎi)票,但只能去火車(chē)站進(jìn)行改簽和退票。在代碼實(shí)現(xiàn)中相當(dāng)于為一個(gè)委托對(duì)象realSubject提供一個(gè)代理對(duì)象PRoxy,通過(guò)proxy可以調(diào)用realSubject的部分功能,并添加一些額外的業(yè)務(wù)處理,同時(shí)可以屏蔽realSubject中未開(kāi)放的接口。
1、RealSubject 是委托類(lèi),Proxy 是代理類(lèi);2、Subject 是委托類(lèi)和代理類(lèi)的接口;3、request() 是委托類(lèi)和代理類(lèi)的共同方法;
具體代碼實(shí)現(xiàn)如下:
interface Subject { void request();}class RealSubject implements Subject { public void request(){ System.out.println("RealSubject"); }}class Proxy implements Subject { private Subject subject; public Proxy(Subject subject){ this.subject = subject; } public void request(){ System.out.println("begin"); subject.request(); System.out.println("end"); }}public class ProxyTest { public static void main(String args[]) { RealSubject subject = new RealSubject(); Proxy p = new Proxy(subject); p.request(); }}靜態(tài)代理實(shí)現(xiàn)中,一個(gè)委托類(lèi)對(duì)應(yīng)一個(gè)代理類(lèi),代理類(lèi)在編譯期間就已經(jīng)確定。
動(dòng)態(tài)代理中,代理類(lèi)并不是在java代碼中實(shí)現(xiàn),而是在運(yùn)行時(shí)期生成,相比靜態(tài)代理,動(dòng)態(tài)代理可以很方便的對(duì)委托類(lèi)的方法進(jìn)行統(tǒng)一處理,如添加方法調(diào)用次數(shù)、添加日志功能等等,動(dòng)態(tài)代理分為jdk動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,下面通過(guò)一個(gè)例子看看如何實(shí)現(xiàn)jdk動(dòng)態(tài)代理。
1、定義業(yè)務(wù)邏輯
public interface Service { //目標(biāo)方法 public abstract void add(); } public class UserServiceImpl implements Service { public void add() { System.out.println("This is add service"); } }2、利用java.lang.reflect.Proxy類(lèi)和java.lang.reflect.InvocationHandler接口定義代理類(lèi)的實(shí)現(xiàn)。
3、使用動(dòng)態(tài)代理
public class ProxyTest { public static void main(String[] args) { Service service = new UserServiceImpl(); MyInvocatioHandler handler = new MyInvocatioHandler(service); Service serviceProxy = (Service)handler.getProxy(); serviceProxy.add(); }}執(zhí)行結(jié)果:
-----before-----This is add service-----end-----代理對(duì)象的生成過(guò)程由Proxy類(lèi)的newProxyInstance方法實(shí)現(xiàn),分為3個(gè)步驟:1、ProxyGenerator.generateProxyClass方法負(fù)責(zé)生成代理類(lèi)的字節(jié)碼,生成邏輯比較復(fù)雜,有興趣的同學(xué)可以繼續(xù)分析源碼 sun.misc.ProxyGenerator;
2、native方法Proxy.defineClass0負(fù)責(zé)字節(jié)碼加載的實(shí)現(xiàn),并返回對(duì)應(yīng)的Class對(duì)象。
3、利用clazz.newInstance反射機(jī)制生成代理類(lèi)的對(duì)象;
反編譯代理類(lèi)為了更清楚的理解動(dòng)態(tài)代理,通過(guò)以下方式把代理類(lèi)字節(jié)碼生成class文件。
byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");out.write(classFile);out.flush();使用 反編譯工具 jad jad com.sun.proxy.$Proxy.1 看看代理類(lèi)如何實(shí)現(xiàn),反編譯出來(lái)的java代碼如下:
從上述代碼可以發(fā)現(xiàn):1、生成的$proxy1繼承自Proxy類(lèi),并實(shí)現(xiàn)了Service接口。2、執(zhí)行代理對(duì)象的方法,其實(shí)就是執(zhí)行InvocationHandle對(duì)象的invoke方法,傳入的參數(shù)分別是當(dāng)前代理對(duì)象,當(dāng)前執(zhí)行的方法和參數(shù)。
super.h.invoke(this, m3, null);jdk動(dòng)態(tài)代理使用的局限性通過(guò)反射類(lèi)Proxy和InvocationHandler回調(diào)接口實(shí)現(xiàn)的jdk動(dòng)態(tài)代理,要求委托類(lèi)必須實(shí)現(xiàn)一個(gè)接口,但事實(shí)上并不是所有類(lèi)都有接口,對(duì)于沒(méi)有實(shí)現(xiàn)接口的類(lèi),便無(wú)法使用該方方式實(shí)現(xiàn)動(dòng)態(tài)代理。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注