說說Java代理模式


代理實現可以分為靜態代理和動態代理。

靜態代理

靜態代理模式其實很常見,比如買火車票這件小事:黃牛相當於是火車站的代理,我們可以通過黃牛買票,但只能去火車站進行改簽和退票。在代碼實現中相當於為一個委托對象realSubject提供一個代理對象proxy,通過proxy可以調用realSubject的部分功能,並添加一些額外的業務處理,同時可以屏蔽realSubject中未開放的接口。


 

1、RealSubject 是委托類,Proxy 是代理類;
2、Subject 是委托類和代理類的接口;
3、request() 是委托類和代理類的共同方法;

具體代碼實現如下:

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(); } }

靜態代理實現中,一個委托類對應一個代理類,代理類在編譯期間就已經確定。

動態代理

動態代理中,代理類並不是在Java代碼中實現,而是在運行時期生成,相比靜態代理,動態代理可以很方便的對委托類的方法進行統一處理,如添加方法調用次數、添加日志功能等等,動態代理分為jdk動態代理和cglib動態代理,下面通過一個例子看看如何實現jdk動態代理。

1、定義業務邏輯

public interface Service { //目標方法 public abstract void add(); } public class UserServiceImpl implements Service { public void add() { System.out.println("This is add service"); } }

2、利用java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口定義代理類的實現。

class MyInvocatioHandler implements InvocationHandler { private Object target; public MyInvocatioHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----before-----"); Object result = method.invoke(target, args); System.out.println("-----end-----"); return result; } // 生成代理對象 public Object getProxy() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?>[] interfaces = target.getClass().getInterfaces(); return Proxy.newProxyInstance(loader, interfaces, this); } }

3、使用動態代理

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(); } }

執行結果:

-----before----- This is add service -----end-----

代理對象的生成過程由Proxy類的newProxyInstance方法實現,分為3個步驟:
1、ProxyGenerator.generateProxyClass方法負責生成代理類的字節碼,生成邏輯比較復雜,有興趣的同學可以繼續分析源碼 sun.misc.ProxyGenerator

// proxyName:格式如 "com.sun.proxy.$Proxy.1"; // interfaces:代理類需要實現的接口數組; // accessFlags:代理類的訪問標識; byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

2、native方法Proxy.defineClass0負責字節碼加載的實現,並返回對應的Class對象。

Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

3、利用clazz.newInstance反射機制生成代理類的對象;

反編譯代理類
為了更清楚的理解動態代理,通過以下方式把代理類字節碼生成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 看看代理類如何實現,反編譯出來的java代碼如下:

public final class $proxy1 extends Proxy implements Service { public $proxy1(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void add() { try { super.h.invoke(this, m3, null); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } private static Method m1; private static Method m2; private static Method m3; private static Method m0; static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch(ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }

從上述代碼可以發現:
1、生成的$proxy1繼承自Proxy類,並實現了Service接口。
2、執行代理對象的方法,其實就是執行InvocationHandle對象的invoke方法,傳入的參數分別是當前代理對象,當前執行的方法和參數。

super.h.invoke(this, m3, null);

jdk動態代理使用的局限性
通過反射類ProxyInvocationHandler回調接口實現的jdk動態代理,要求委托類必須實現一個接口,但事實上並不是所有類都有接口,對於沒有實現接口的類,便無法使用該方方式實現動態代理。



文/占小狼(簡書作者)
原文鏈接:http://www.jianshu.com/p/a1d094fc6c00
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM