設(shè)計(jì)模式(七-擴(kuò)展篇)——?jiǎng)討B(tài)代理

本文屬于系列文章《設(shè)計(jì)模式》,附上文集鏈接

本文集代理模式請(qǐng)看此處

前言

上一篇寫的那個(gè)代理模式,是屬于靜態(tài)代理。假設(shè)一種場(chǎng)景,假設(shè)我們要為很多類的很多方法添加前置和后置方法(別忘了代理模式是為了控制),按照靜態(tài)代理的方法,我們就得為所有類的所有方法一個(gè)一個(gè)地去實(shí)現(xiàn)需求,想想這工程量就覺得可怕,所以就有了很優(yōu)美的實(shí)現(xiàn)方式,叫做動(dòng)態(tài)代理。

JDK代理

且看代碼

// 抽象接口
public interface MyInterface {
    void hello(int i, int j);
    void hello(int i, int j, int k);
}
// 接口實(shí)現(xiàn)類,也是我們的代理目標(biāo)類
public class MySubject implements MyInterface{
    @Override
    public void hello(int i, int j) {
        System.out.println("hello(): "+i+"-"+j);
    }
    @Override
    public void hello(int i, int j, int k) {
        System.out.println("hello(): "+i+"-"+j+"~"+k);
    }
}
// 這個(gè)是關(guān)鍵,被代理對(duì)象方法的實(shí)際執(zhí)行地方,通過反射實(shí)現(xiàn)
public class MyInvocationHandler implements InvocationHandler{
    // 被代理的目標(biāo)對(duì)象
    private Object target;
    MyInvocationHandler(Object target) {  
        super();  
        this.target = target;  
    } 
    @Override
    /\*\*
     \* proxy 代理的目標(biāo)對(duì)象
     \* method 代理的目標(biāo)對(duì)象的方法
     \*  args,方法的參數(shù)
     \*/
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法: " + method.getName() + "執(zhí)行前");
        StringBuilder sb = new StringBuilder("方法的參數(shù):");
        for (Object arg : args) {
            sb.append(arg.toString()+" ");
        }
        System.out.println(sb.toString());
        Object result = method.invoke(target, args);
        System.out.println("方法: " + method.getName() + "執(zhí)行后");
        return result;
    }
}
// 場(chǎng)景類
public class Client {
    public static void main(String[] args) {
        MyInterface mySubject = new MySubject();
        MyInvocationHandler handler = new MyInvocationHandler(mySubject);
        MyInterface mySubjectProxy = (MyInterface) Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),  
                mySubject.getClass().getInterfaces(), handler);
proxy.getProxyObject();
        mySubjectProxy.hello(5,98);
        System.out.println("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*");
        mySubjectProxy.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

比較關(guān)鍵的就三個(gè)地方:

  • 一、被代理的目標(biāo)對(duì)象必須實(shí)現(xiàn)接口,即上文的<code>MySubject</code>和<code>MyInterface</code>。
  • 二、實(shí)現(xiàn)InvocationHandler接口的handler類,即上文中的<code>public class MyInvocationHandler implements InvocationHandler</code>。方法的參數(shù)說明在上面也有了,這里就不在闡述了。
  • 三、使用Proxy類獲取到目標(biāo)對(duì)象的代理對(duì)象,即上文中的Proxy.newProxyInstance(mySubject.getClass().getClassLoader(),mySubject.getClass().getInterfaces(), handler);

之前也想過Proxy是怎么實(shí)現(xiàn)這個(gè)代理過程的,結(jié)果在這里看到了。
回到上面的代碼,其實(shí)上面的代碼還可以封裝下,比如變成下面的樣子:

// 接口類,接口實(shí)現(xiàn)類,handler類都沒變化,引入下面的類
// 簡(jiǎn)單的動(dòng)態(tài)代理類,傳入一個(gè)目標(biāo)對(duì)象參數(shù),得到其代理對(duì)象
public class DynamicProxy{
    private MyInvocationHandler handler;
    private Object target;
    public DynamicProxy(Object _target) {
        this.target = _target;
        handler = new MyInvocationHandler(_target);
    }
    public Object getProxyObject(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }
}
// 場(chǎng)景類
public class Client {
    public static void main(String[] args) {
        MyInterface mySubject = new MySubject();
        **DynamicProxy proxy = new DynamicProxy(mySubject);
        MyInterface mySubjectProxy = (MyInterface) proxy.getProxyObject();**
        mySubjectProxy.hello(5,98);
        System.out.println("******************");
        mySubjectProxy.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
*********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

上面場(chǎng)景類的代碼,用自然語言解釋挺流暢的,就是創(chuàng)建代理類對(duì)象,通過代理類對(duì)象獲取被代理對(duì)象的代理對(duì)象。反正封裝方法多種多樣,上面其實(shí)其中一種,有意者多試試唄。
上面就是JDK代理,但是JDK代理有一個(gè)限制,就是上面說到的關(guān)鍵點(diǎn)的第一點(diǎn),JDK代理無法脫離接口而實(shí)現(xiàn),要為某個(gè)目標(biāo)對(duì)象實(shí)現(xiàn)JDK代理,該目標(biāo)對(duì)象至少實(shí)現(xiàn)一個(gè)接口,那如果要為沒實(shí)現(xiàn)接口的類實(shí)現(xiàn)代理,怎么辦,就是我們的Cglib代理。

cglib代理

且看代碼

// 我們的代理目標(biāo)類,這次是沒有實(shí)現(xiàn)接口的
public class MySubject {
    public void hello(int i, int j) {
        System.out.println("hello(): "+i+"-"+j);
    }
    public void hello(int i, int j, int k) {
        System.out.println("hello(): "+i+"-"+j+"~"+k);
    }
}
// cglib代理類
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法: " + method.getName() + "執(zhí)行前");
        StringBuilder sb = new StringBuilder("方法的參數(shù):");
        for (Object arg : args) {
            sb.append(arg.toString() + " ");
        }
        System.out.println(sb.toString());
        Object obj = methodProxy.invokeSuper(o, args);
        System.out.println("方法: " + method.getName() + "執(zhí)行后");
        return obj;
    }
}
// 場(chǎng)景類
public class Client {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MySubject.class);
        enhancer.setCallback(cglibProxy);
        // 得到代理對(duì)象
        MySubject o = (MySubject) enhancer.create();
        o.hello(5, 98);
        System.out.println("******************");
        o.hello(5, 23, 54);
    }
}
結(jié)果:
方法: hello執(zhí)行前
方法的參數(shù):5 98 
hello(): 5-98
方法: hello執(zhí)行后
**********************
方法: hello執(zhí)行前
方法的參數(shù):5 23 54 
hello(): 5-23~54
方法: hello執(zhí)行后

cglib是提供動(dòng)態(tài)代理的第三方庫,并不是jdk給我們代理,所以需要自己解決導(dǎo)包的問題。
這里,cglib設(shè)計(jì)的幾個(gè)核心類,分別是:

  • net.sf.cglib.proxy.Enhancer – 主要的增強(qiáng)類
  • net.sf.cglib.proxy.MethodInterceptor – 主要的方法攔截類,它是Callback接口的子接口,需要用戶實(shí)現(xiàn)
  • net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method類的代理類,可以方便的實(shí)現(xiàn)對(duì)源對(duì)象方法的調(diào)用

核心方法就是<code>public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable </code>,第一個(gè)參數(shù)是代理對(duì)象,第二和第三個(gè)參數(shù)分別是攔截的方法和方法的參數(shù),第四個(gè)是方法的代理對(duì)象。這個(gè)就是cglib代理。

以上就是動(dòng)態(tài)代理。

水平有限,難免有錯(cuò),還請(qǐng)?jiān)u論區(qū)指責(zé)下

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,837評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,740評(píng)論 18 399
  • 一> 代理模式 概述 代理(Proxy)是一種設(shè)計(jì)模式, 提供了對(duì)目標(biāo)對(duì)象另外的訪問方式;即通過代理訪問目標(biāo)對(duì)象。...
    奮斗的老王閱讀 1,131評(píng)論 0 50
  • 這今天,#田生萬物#的一群女人被一個(gè)女人感動(dòng)著,她是鄭州親子盟、田生萬物創(chuàng)始人草莓,一個(gè)孩子的母親,被大家親切的稱...
    吃貨蘇小妹閱讀 264評(píng)論 0 0
  • 你聽過卓依婷的那首經(jīng)典歌曲《電話情思》嗎?歌詞里是這么唱的:好想好想給你打個(gè)電話,也想和你悄悄地說些知心話。一遍一...
    可桐閱讀 479評(píng)論 4 7