動態(tài)代理實現(xiàn)的兩種方式

1.前言

我們知道不管靜態(tài)代理還是動態(tài)代理,實際上都是對于源對象的一種控制,通過代理對象來間接訪問目標對象熄捍。但是靜態(tài)代理有以下兩個問題
靜態(tài)代理存在的問題是:
1)一旦接口新增或者修改,那么代理對象和被代理對象就得去適配修改
2)靜態(tài)代理是在代碼編寫時赏半,去生成的,class文件必然會造成類爆炸的風險
有沒有方案解決以上的問題呢?顯然是有的,那就是動態(tài)代理饲握。動態(tài)代理私杜,顧名思義,就是在運行時去動態(tài)生成代理救欧,我們下面分別說兩種實現(xiàn)方式,分別為JDK和Cglib锣光。

2.兩種實現(xiàn)方案

2.1 實現(xiàn)方案--JDK動態(tài)代理

如上面所說笆怠,一般被代理的對象會實現(xiàn)某個接口,我們是否可以借用代理模式的這個特點做點文章呢誊爹?
JDK給我們提供了一些API蹬刷,可以實現(xiàn)動態(tài)代理,InvocationHandler和Proxy.newProxyInstance來實現(xiàn)频丘。
動態(tài)代理類:

package com.itbird.design.proxy.demo.dynamic.v1;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 代理對象
 * Created by itbird on 2022/7/4
 */
public class ProxyObject implements InvocationHandler {
    Object proxy;

    public ProxyObject(Object proxy) {
        this.proxy = proxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(proxy, args);
    }
}

代碼很簡單办成,就是實現(xiàn)InvocationHandler接口,重寫invoke方法搂漠,這里有一個關鍵的構造函數(shù)迂卢,就是要入?yún)⒃磳ο蟆K晕覀兛匆幌抡{(diào)用桐汤。

package com.itbird.design.proxy.demo.dynamic.v1;

import java.lang.reflect.Proxy;

/**
 * Created by itbird on 2022/7/4
 */
public class Client {
    public void main() {
        SourceObject sourceObject = new SourceObject();
        // 返回的是 IBank 的一個實例對象而克,這個對象是由 Java 給我們創(chuàng)建的 ,調(diào)用的是 jni,通過反射+classloader加載
        IObject proxy = (IObject) Proxy.newProxyInstance(IObject.class.getClassLoader(), // ClassLoader
                new Class[]{IObject.class},  //目標接口
                new ProxyObject(sourceObject)); //替換代理
        proxy.methodA();
    }
}

是否很簡單怔毛,如果這時接口新增或者修改员萍,大家發(fā)現(xiàn)沒有,不再需要去修改代理類拣度。因為其本質(zhì)是碎绎,通過反射+classloader去實現(xiàn)的,所以不依賴于對象抗果。
但是依然有一個問題筋帖,就是Proxy.newProxyInstance使用上有一個弊端,就是必須面向接口窖张,如果源對象也就是被代理對象幕随,如果沒有實現(xiàn)某個接口,這時不就GG了宿接。該怎么做呢赘淮?不要著急,Cglib可以幫我們解決這個問題睦霎。

2.2 實現(xiàn)方案--Cglib

我們知道一個對象在內(nèi)存中存儲梢卸,一般就是在棧或者堆上副女,那是否有一種方法或者組件蛤高,可以直接從內(nèi)存copy這個對象,實現(xiàn)深克隆,也就是在內(nèi)存中在copy一個出來戴陡。

Cglib代理模式的基本介紹
1) JDK動態(tài)代理模式要求目標對象是實現(xiàn)一個接口,但是有時候目標對象只是一個單獨的對象,并沒有實現(xiàn)任何的接口,這個時候可使用目標對象子類來實現(xiàn)代理-這就是Cglib代理
2)Cglib代理也叫作子類代理,它是在內(nèi)存中構建一個子類對象從而實現(xiàn)對目標對象功能擴展
3)Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP塞绿,實現(xiàn)方法攔截
4)Cglib包的底層是通過使用字節(jié)碼處理框架ASM來轉換字節(jié)碼并生成新的類
5)Cglib由于是基于字節(jié)碼的,顯然這時android就不能使用了恤批,因為android是dex文件异吻,這怎么辦?沒關系喜庞,有 dexmaker 和 cglib-for-android 庫诀浪。

Cglib代理如何實現(xiàn)呢?

第一步:引入cglib的android jar文件
定義源對象延都,可以看到此時的被代理對象雷猪,并未實現(xiàn)任何接口
package com.itbird.design.proxy.demo.dynamic.v2;

/**
 * 原始對象,預對其訪問晰房,加以控制的對象求摇,沒有實現(xiàn)接口
 * Created by itbird on 2022/7/4
 */
public class SourceObject  {
    public void methodA() {
        //TODO 具體的實現(xiàn)
    }

    public void methodB() {
        //TODO 具體的實現(xiàn)
    }

    public void methodC() {
        //TODO 具體的實現(xiàn)
    }

    public void methodD() {
        //TODO 具體的實現(xiàn)
    }

    public void methodE() {
        //TODO 具體的實現(xiàn)
    }
}

第二步:代碼實現(xiàn),首先使用Cglib實現(xiàn)方法攔截

package com.itbird.design.proxy.demo.dynamic.v2;

import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;

/**
 * 代理對象
 * Created by itbird on 2022/7/4
 */
public class ProxyMethodInterceptor implements MethodInterceptor {

    /**
     * 重寫方法攔截在方法前和方法后加入業(yè)務
     * Object obj為目標對象
     * Method method為目標方法
     * Object[] params 為參數(shù)嫉你,
     * MethodProxy proxy CGlib方法代理對象
     */
    @Override
    public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
        //參數(shù):Object為由CGLib動態(tài)生成的代理類實例月帝,Method為上文中實體類所調(diào)用的被代理的方法引用,Object[]為參數(shù)值列表幽污,MethodProxy為生成的代理類對方法的代理引用嚷辅。
        //返回:從代理實例的方法調(diào)用返回的值。
        //其中距误,proxy.invokeSuper(obj,arg) 調(diào)用代理類實例上的proxy方法的父類方法(即實體類TargetObject中對應的方法)
        System.out.println("調(diào)用前");
        Object result = methodProxy.invokeSuper(o,objects);
        System.out.println(" 調(diào)用后"+result);
        return result;
    }
}

第三步簸搞,調(diào)用

package com.itbird.design.proxy.demo.dynamic.v2;


import android.content.Context;

import leo.android.cglib.proxy.Enhancer;

/**
 * Created by itbird on 2022/7/4
 */
public class Client {
    public void main(Context context) {
        //Enhancer類是CGLib中的一個字節(jié)碼增強器
        Enhancer enhancer = new Enhancer(context);
        //將被代理類TargetObject設置成父類,然后設置攔截器TargetInterceptor
        enhancer.setSuperclass(SourceObject.class);
        enhancer.setInterceptor(new ProxyMethodInterceptor());

        //執(zhí)行enhancer.create()動態(tài)生成一個代理類准潭,并從Object強制轉型成父類型TargetObject
        SourceObject object = (SourceObject) enhancer.create();
        //在代理類上調(diào)用方法
        object.methodA();
    }
}

大家發(fā)現(xiàn)趁俊,此時實現(xiàn)了源對象的方法調(diào)用。

鏈接:https://juejin.cn/post/7169416619691606023

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刑然,一起剝皮案震驚了整個濱河市寺擂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泼掠,老刑警劉巖怔软,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異择镇,居然都是意外死亡挡逼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門腻豌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來家坎,“玉大人嘱能,你說我怎么就攤上這事∈瑁” “怎么了惹骂?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長订框。 經(jīng)常有香客問我析苫,道長,這世上最難降的妖魔是什么穿扳? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮国旷,結果婚禮上矛物,老公的妹妹穿的比我還像新娘。我一直安慰自己跪但,他們只是感情好履羞,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屡久,像睡著了一般忆首。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上被环,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天糙及,我揣著相機與錄音,去河邊找鬼浸锨。 笑死,一個胖子當著我的面吹牛柱搜,可吹牛的內(nèi)容都是我干的剥险。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼健爬,長吁一口氣:“原來是場噩夢啊……” “哼夫凸!你這毒婦竟也來了?” 一聲冷哼從身側響起夭拌,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒜绽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲雅,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡相赁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唤衫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绵脯。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛆挫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情瞧剖,我是刑警寧澤畜挨,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站巴元,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呕缭。R本人自食惡果不足惜修己,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一睬愤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尤辱,春花似錦厢岂、人聲如沸阳距。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至圃酵,卻和暖如春馍管,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咽斧。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工躬存, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宛逗。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓盾剩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屎暇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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