說說Java代理模式

在平時寫代碼時,經(jīng)常會用到各種設(shè)計模式,其中一種就是代理模式,代理實現(xiàn)可以分為靜態(tài)代理和動態(tài)代理。

靜態(tài)代理

靜態(tài)代理的模式在平時生活中也很常見,比如買火車票這件小事,黃牛相當(dāng)于是火車站的代理,我們可以通過黃?;蛘叽埸c進行買票行為,但只能去火車站進行改簽和退票,因為只有火車站才有改簽和退票的方法。

在代碼實現(xiàn)中相當(dāng)于為一個委托對象realSubject提供一個代理對象proxy,通過proxy可以調(diào)用realSubject的部分功能(買票),并添加一些額外的業(yè)務(wù)處理(收取手續(xù)費),同時可以屏蔽realSubject中未開放的接口(改簽和退票)。

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

具體代碼實現(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)代理實現(xiàn)中,一個委托類對應(yīng)一個代理類,代理類在編譯期間就已經(jīng)確定。

動態(tài)代理

動態(tài)代理中,代理類并不是在Java代碼中實現(xiàn),而是在運行時期生成,相比靜態(tài)代理,動態(tài)代理可以很方便的對委托類的方法進行統(tǒng)一處理,如添加方法調(diào)用次數(shù)、添加日志功能等等,動態(tài)代理分為jdk動態(tài)代理和cglib動態(tài)代理,下面通過一個例子看看如何實現(xiàn)jdk動態(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類和java.lang.reflect.InvocationHandler接口定義代理類的實現(xiàn)。

    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、使用動態(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-----

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

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

2、native方法Proxy.defineClass0負(fù)責(zé)字節(jié)碼加載的實現(xiàn),并返回對應(yīng)的Class對象。

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

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

反編譯代理類
為了更清楚的理解動態(tà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 看看代理類如何實現(xiàn),反編譯出來的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());
            }
        }
    }
    

從上述代碼可以發(fā)現(xiàn):

1、生成的$proxy1繼承自Proxy類,并實現(xiàn)了Service接口。
2、執(zhí)行代理對象的方法,其實就是執(zhí)行InvocationHandle對象的invoke方法,傳入的參數(shù)分別是當(dāng)前代理對象,當(dāng)前執(zhí)行的方法和參數(shù)。

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

JDK動態(tài)代理局限性
通過反射類ProxyInvocationHandler回調(diào)接口實現(xiàn)的jdk動態(tài)代理,要求委托類必須實現(xiàn)一個接口,但事實上并不是所有類都有接口,對于沒有實現(xiàn)接口的類,便無法使用該方方式實現(xiàn)動態(tài)代理。

轉(zhuǎn)載地址
占小狼blog

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容