說說Java代理模式

來源:互聯網
上載者:User

標籤:參數   val   調用   throws   request   als   current   方法調用   .com   

安利一位簡書佔小狼。目前簡書中的文章以Java方面的技術為主,感覺寫的非常不錯。歡迎關注~

本文即出自該作者,原本地址:http://www.jianshu.com/p/a1d094fc6c00

若轉載,請聯絡簡書作者

 

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

靜態代理

靜態代理模式其實很常見,比如買火車票這件小事:黃牛相當於是火車站的代理,我們可以通過黃牛買票,但只能去火車站進行改簽和退票。在代碼實現中相當於為一個委派物件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動態代理,要求委託類必須實現一個介面,但事實上並不是所有類都有介面,對於沒有實現介面的類,便無法使用該方方式實現動態代理。

說說Java代理模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.