靜態(tài)代理和動態(tài)代理有什么區(qū)別啄清?--樂字節(jié)java

代理模式

? 代理模式在 Java 開發(fā)中是一種比較常見的設(shè)計(jì)模式六水。設(shè)計(jì)目的旨在為服務(wù)類與客戶類之間插入其他功能,插入的功能對于調(diào)用者是透明的辣卒,起到偽裝控制的作用缩擂。如租房的例子:房客、中介添寺、房東。對應(yīng)于代理模式中即:客戶類懈费、代理類 计露、委托類(被代理類)。

? 為某一個(gè)對象(委托類)提供一個(gè)代理(代理類)憎乙,用來控制對這個(gè)對象的訪問票罐。委托類和代理類有一個(gè)共同的父類或父接口。代理類會對請求做預(yù)處理泞边、過濾该押,將請求分配給指定對象。

? 生活中常見的代理情況:

? 租房中介阵谚、婚慶公司等

? 代理模式的兩個(gè)設(shè)計(jì)原則:

? 1. 代理類 與 委托類 具有相似的行為(共同)

? 2. 代理類增強(qiáng)委托類的行為

SpringAOP-01.png

? 常用的代理模式:

? 1. 靜態(tài)代理

? 2. 動態(tài)代理

靜態(tài)代理

? 某個(gè)對象提供一個(gè)代理蚕礼,代理角色固定,以控制對這個(gè)對象的訪問梢什。 代理類和委托類有共同的父類或父接口奠蹬,這樣在任何使用委托類對象的地方都可以用代理對象替代。代理類負(fù)責(zé)請求的預(yù)處理嗡午、過濾囤躁、將請求分派給委托類處理、以及委托類執(zhí)行完請求后的后續(xù)處理。

代理的三要素

? a狸演、有共同的行為(結(jié)婚) - 接口

? b言蛇、目標(biāo)角色(新人) - 實(shí)現(xiàn)行為

? c、代理角色(婚慶公司) - 實(shí)現(xiàn)行為 增強(qiáng)目標(biāo)對象行為

靜態(tài)代理的特點(diǎn)

? 1宵距、目標(biāo)角色固定

? 2腊尚、在應(yīng)用程序執(zhí)行前就得到目標(biāo)角色

? 3、代理對象會增強(qiáng)目標(biāo)對象的行為

? 4消玄、有可能存在多個(gè)代理 引起"類爆炸"(缺點(diǎn))

靜態(tài)代理的實(shí)現(xiàn)

定義行為(共同) 定義接口

/**
 * 定義行為
 */
public interface Marry {
    public void toMarry();
}

目標(biāo)對象(實(shí)現(xiàn)行為)

/**
 * 靜態(tài)代理 ——> 目標(biāo)對象
 */
public class You implements  Marry {
    // 實(shí)現(xiàn)行為
    @Override
    public void toMarry() {
        System.out.println("我要結(jié)婚了...");
    }
}

代理對象(實(shí)現(xiàn)行為跟伏、增強(qiáng)目標(biāo)對象的行為)

/**
 * 靜態(tài)代理 ——> 代理對象
 */
public class MarryCompanyProxy implements Marry {

    // 目標(biāo)對象
    private Marry marry;
    // 通過構(gòu)造器將目標(biāo)對象傳入
    public MarryCompanyProxy(Marry marry) {
        this.marry = marry;
    }

    // 實(shí)現(xiàn)行為
    @Override
    public void toMarry() {
        // 增強(qiáng)行為
        before();
                
        // 執(zhí)行目標(biāo)對象中的方法
        marry.toMarry();
        
        // 增強(qiáng)行為
        after();
    }

    /**
     * 增強(qiáng)行為
     */
    private void after() {
        System.out.println("新婚快樂,早生貴子翩瓜!");
    }

    /**
     * 增強(qiáng)行為
     */
    private void before() {
        System.out.println("場地正在布置中...");
    }
}

通過代理對象實(shí)現(xiàn)目標(biāo)對象的功能

// 目標(biāo)對象
You you = new You();
// 構(gòu)造代理角色同時(shí)傳入真實(shí)角色
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通過代理對象調(diào)用目標(biāo)對象中的方法
marryCompanyProxy.toMarry();

? 靜態(tài)代理對于代理的角色是固定的受扳,如dao層有20個(gè)dao類,如果要對方法的訪問權(quán)限進(jìn)行代理兔跌,此時(shí)需要?jiǎng)?chuàng)建20個(gè)靜態(tài)代理角色勘高,引起類爆炸,無法滿足生產(chǎn)上的需要坟桅,于是就催生了動態(tài)代理的思想华望。

如有疑問,可加入群:10803-55292,輸入暗號13仅乓,即可有大佬幫助

動態(tài)代理

? 相比于靜態(tài)代理赖舟,動態(tài)代理在創(chuàng)建代理對象上更加的靈活,動態(tài)代理類的字節(jié)碼在程序運(yùn)行時(shí)夸楣,由Java反射機(jī)制動態(tài)產(chǎn)生宾抓。它會根據(jù)需要,通過反射機(jī)制在程序運(yùn)行期豫喧,動態(tài)的為目標(biāo)對象創(chuàng)建代理對象石洗,無需程序員手動編寫它的源代碼。動態(tài)代理不僅簡化了編程工作紧显,而且提高了軟件系統(tǒng)的可擴(kuò)展性讲衫,因?yàn)榉瓷錂C(jī)制可以生成任意類型的動態(tài)代理類。代理的行為可以代理多個(gè)方法孵班,即滿足生產(chǎn)需要的同時(shí)又達(dá)到代碼通用的目的涉兽。

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

? 1. JDK 動態(tài)代理

? 2. CGLIB動態(tài)代理

動態(tài)代理的特點(diǎn)

  1. 目標(biāo)對象不固定
  2. 在應(yīng)用程序執(zhí)行時(shí)動態(tài)創(chuàng)建目標(biāo)對象
  3. 代理對象會增強(qiáng)目標(biāo)對象的行為

JDK動態(tài)代理

? 注:JDK動態(tài)代理的目標(biāo)對象必須有接口實(shí)現(xiàn)

newProxyInstance

Proxy類:

? Proxy類是專門完成代理的操作類,可以通過此類為一個(gè)或多個(gè)接口動態(tài)地生成實(shí)現(xiàn)類重父,此類提供了如下操作方法:

/*
    返回一個(gè)指定接口的代理類的實(shí)例方法調(diào)用分派到指定的調(diào)用處理程序花椭。 (返回代理對象)
        loader:一個(gè)ClassLoader對象,定義了由哪個(gè)ClassLoader對象來對生成的代理對象進(jìn)行加載
        interfaces:一個(gè)Interface對象的數(shù)組房午,表示的是我將要給我需要代理的對象提供一組什么接口矿辽,如果                    我提供了一組接口給它,那么這個(gè)代理對象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這                    組接口中的方法了
        h:一個(gè)InvocationHandler接口袋倔,表示代理實(shí)例的調(diào)用處理程序?qū)崿F(xiàn)的接口雕蔽。每個(gè)代理實(shí)例都具有一個(gè)           關(guān)聯(lián)的調(diào)用處理程序。對代理實(shí)例調(diào)用方法時(shí)宾娜,將對方法調(diào)用進(jìn)行編碼并將其指派到它的調(diào)用處理程序            的 invoke 方法(傳入InvocationHandler接口的子類)
*/
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

獲取代理對象

public class JdkHandler implements InvocationHandler {

    // 目標(biāo)對象
    private Object target; // 目標(biāo)對象的類型不固定批狐,創(chuàng)建時(shí)動態(tài)生成
    // 通過構(gòu)造器將目標(biāo)對象賦值
    public JdkHandler(Object target) {
        this.target = target;
    }

    /**
     *  1、調(diào)用目標(biāo)對象的方法(返回Object)
     *  2前塔、增強(qiáng)目標(biāo)對象的行為
     * @param proxy 調(diào)用該方法的代理實(shí)例
     * @param method  目標(biāo)對象的方法
     * @param args  目標(biāo)對象的方法形參
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 增強(qiáng)行為
        System.out.println("==============方法前執(zhí)行");

        // 調(diào)用目標(biāo)對象的方法(返回Object)
        Object result = method.invoke(target,args);

        // 增強(qiáng)行為
        System.out.println("方法后執(zhí)行==============");

        return result;
    }


    /**
     * 得到代理對象
     * public static Object newProxyInstance(ClassLoader loader,
     *                                       Class<?>[] interfaces,
     *                                       InvocationHandler h)
     *      loader:類加載器
     *      interfaces:接口數(shù)組
     *      h:InvocationHandler接口 (傳入InvocationHandler接口的實(shí)現(xiàn)類)
     *
     *
     * @return
     */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
}

通過代理對象實(shí)現(xiàn)目標(biāo)對象的功能

// 目標(biāo)對象
You you = new You();
// 獲取代理對象
JdkHandler jdkHandler = new JdkHandler(you);
Marry marry = (Marry) jdkHandler.getProxy();
// 通過代理對象調(diào)用目標(biāo)對象中的方法
marry.toMarry();    
問:Java動態(tài)代理類中的invoke是怎么調(diào)用的嚣艇?

答:在生成的動態(tài)代理類$Proxy0.class中,構(gòu)造方法調(diào)用了父類Proxy.class的構(gòu)造方法华弓,給成員變量invocationHandler賦值食零,$Proxy0.class的static模塊中創(chuàng)建了被代理類的方法,調(diào)用相應(yīng)方法時(shí)方法體中調(diào)用了父類中的成員變量InvocationHandler的invoke()方法寂屏。

注:JDK的動態(tài)代理依靠接口實(shí)現(xiàn)贰谣,如果有些類并沒有接口實(shí)現(xiàn),則不能使用JDK代理迁霎。

CGLIB 動態(tài)代理

? JDK的動態(tài)代理機(jī)制只能代理實(shí)現(xiàn)了接口的類吱抚,而不能實(shí)現(xiàn)接口的類就不能使用JDK的動態(tài)代理,cglib是針對類來實(shí)現(xiàn)代理的考廉,它的原理是對指定的目標(biāo)類生成一個(gè)子類秘豹,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),但因?yàn)椴捎玫氖抢^承昌粤,所以不能對final修飾的類進(jìn)行代理憋肖。

添加依賴

在pom.xml文件中引入cglib的相關(guān)依賴

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>

定義類

實(shí)現(xiàn)MethodInterceptor接口

public class CglibInterceptor implements MethodInterceptor {

    // 目標(biāo)對象
    private Object target;
    // 通過構(gòu)造器傳入目標(biāo)對象
    public CglibInterceptor(Object target) {
        this.target = target;
    }

    /**
     * 獲取代理對象
     * @return
     */
    public Object getProxy() {
        // 通過Enhancer對象的create()方法可以生成一個(gè)類,用于生成代理對象
        Enhancer enhancer = new Enhancer();
        // 設(shè)置父類 (將目標(biāo)類作為其父類)
        enhancer.setSuperclass(target.getClass());
        // 設(shè)置攔截器 回調(diào)對象為本身對象
        enhancer.setCallback(this);
        // 生成一個(gè)代理類對象婚苹,并返回
        return enhancer.create();
    }

    /**
     * 攔截器
     *  1、目標(biāo)對象的方法調(diào)用
     *  2鸵膏、增強(qiáng)行為
     * @param object  由CGLib動態(tài)生成的代理類實(shí)例
     * @param method  實(shí)體類所調(diào)用的被代理的方法引用
     * @param objects 參數(shù)值列表
     * @param methodProxy  生成的代理類對方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, 
                            MethodProxy methodProxy) throws Throwable {

        // 增強(qiáng)行為
        System.out.println("==============方法前執(zhí)行");

        // 調(diào)用目標(biāo)對象的方法(返回Object)
        Object result = methodProxy.invoke(target,objects);

        // 增強(qiáng)行為
        System.out.println("方法后執(zhí)行==============");

        return result;
    }

}

調(diào)用方法

// 目標(biāo)對象
You you = new You();
CglibInterceptor cglibInterceptor = new CglibInterceptor(you);
Marry marry = (Marry) cglibInterceptor.getProxy();
marry.toMarry();

User user = new User();
CglibInterceptor cglibInterceptor = new CglibInterceptor(user);
User u = (User) cglibInterceptor.getProxy();
u.test();

JDK代理與CGLIB代理的區(qū)別

  • JDK動態(tài)代理實(shí)現(xiàn)接口膊升,Cglib動態(tài)代理繼承思想
  • JDK動態(tài)代理(目標(biāo)對象存在接口時(shí))執(zhí)行效率高于Ciglib
  • 如果目標(biāo)對象有接口實(shí)現(xiàn),選擇JDK代理谭企,如果沒有接口實(shí)現(xiàn)選擇Cglib代理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廓译,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子债查,更是在濱河造成了極大的恐慌非区,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盹廷,死亡現(xiàn)場離奇詭異征绸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門管怠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淆衷,“玉大人,你說我怎么就攤上這事渤弛∽U” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵她肯,是天一觀的道長佳头。 經(jīng)常有香客問我,道長晴氨,這世上最難降的妖魔是什么康嘉? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮瑞筐,結(jié)果婚禮上凄鼻,老公的妹妹穿的比我還像新娘。我一直安慰自己聚假,他們只是感情好块蚌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著膘格,像睡著了一般峭范。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瘪贱,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天纱控,我揣著相機(jī)與錄音,去河邊找鬼菜秦。 笑死甜害,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的球昨。 我是一名探鬼主播尔店,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼主慰!你這毒婦竟也來了嚣州?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤共螺,失蹤者是張志新(化名)和其女友劉穎该肴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藐不,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匀哄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年秦效,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拱雏。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棉安,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铸抑,到底是詐尸還是另有隱情贡耽,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布鹊汛,位于F島的核電站蒲赂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏刁憋。R本人自食惡果不足惜滥嘴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望至耻。 院中可真熱鬧若皱,春花似錦、人聲如沸尘颓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疤苹。三九已至互广,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卧土,已是汗流浹背惫皱。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尤莺,地道東北人旅敷。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像颤霎,于是被迫代替她去往敵國和親扫皱。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348