代理模式學習(靜態代理、動態代理【jdk、cglib】)

開場前我們要準備好瓜子花生礦泉水(我們先了解基本知識)

代理模式的必要條件

1. 需要有代理者、被代理人

2. 對於代理人來說某些事情一定要做,但是自己又不想做或者沒辦法去做

3. 被代理人需要提供自己 的信息

圖看的一臉懵逼的我都優點餓了,不如點一份世界上最好喫的飯吧邊喫邊看

距離點餐配送時間以及超時13個半小時,終於外賣小哥到了,把我激動的一開門就大喊 哎呦終於等到你了我的代理人,搞得小哥一臉懵啊,我什麼時候成了你的代理人了。嚇得他趕緊開溜。

外賣小哥 和 點餐人 就是代理模式的體現

1. 外賣小哥、點餐人

2. 點餐人必須要喫飯,但是自己忙着加班沒辦法去店裏喫飯

3. 點餐人提供自己的信息(姓名 電話 位置)

滿足代理模式的必要條件

既然是代理模式那怎麼用代碼寫出這樣一個關係呢,

學習代理第一步先搞懂靜態代理。

靜態代理 (一個代理類只能代理一個類型)

優點:是效率最高的一種方式,因爲所有的類都是已經編寫完成的,客戶端只需要取得代理對象並且執行即可。

缺點:1)代理類和委託類實現了相同的接口,代理類通過委託類實現了相同的方法。這樣就出現了大量的代碼重複。如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的複雜度。

           2)代理對象只服務於一種類型的對象,如果要服務多類型的對象。勢必要爲每一種對象都進行代理,任務量太大。複用性太差。

哎呦!這概念也太難記了吧,不管了我先往後看。

1. 先定義一個接口 需要讓 代理者和被代理者共同實現該接口

package staticproxy;

public interface Eat {
    void eatFood();
}

2. 被代理者實現接口做自己的事情

package staticproxy;

// 點外賣的人
public class Customer implements Eat{
    @Override
    public void eatFood() {
        System.out.println("我餓死了,我點的世界上最好喫的飯怎麼還沒到?");
    }
}

3. 代理者

package staticproxy;

//外賣配送員
public class DeliveryClerk implements Eat{
    // 我的外賣要送給這個 target 的人
    private Person target;

    // 當我接到送餐命令時要給我一個訂外賣人的信息
    public DeliveryClerk(Eat target) {
        this.target = target;
    }

    @Override
    public void eatFood() {
        System.out.println("我要去送餐了");
        target.eatFood();
        System.out.println("你好你的外賣");

    }
}

4. 客戶端調用 客戶端並不需要知道什麼時候調用被代理者的方法只要關心代理者

package staticproxy;

public class Client {
    public static void main(String[] args) {
        // 訂的外賣交給外賣小哥給我送過來
        Eat e = new DeliveryClerk(new Customer());
        e.eatFood();
    }
}

這時候喫飽喝足了 心想自己還沒有女朋友怎麼辦呢?這麼大年齡了都該結婚了。我整天加班哪有時間出去找女朋友啊!這時候呢有個好消息隔壁開了一個婚姻介紹所。我立馬去填了個徵婚信息交給他們然後自己就回到公司上班了。這個事情同樣符合代理模式也滿足代理模式的必要條件1. 我和婚姻介紹所 2. 我沒時間找女朋友但是該結婚了又必須找女朋友 3. 我給婚姻介紹所提供我的基本信息和徵婚條件膚白貌美大長腿

因爲需求的增多就又需要一個代理者(婚姻介紹所) 和 一個共同的實現類  我身爲一個錢多話少死得早的程序員,我這麼多需求我都要挨個找相應的代理者這也太麻煩了吧,有沒有這麼一種可能呢 我需求變了不需要我自己到處找代理者。懶纔是我的本能麼。爲了懶得有特色我就還是找有沒有什麼方式可以讓我偷個懶啊。突然有一天我得到一個消息,村裏開了一個一條龍代理所,什麼都能幫你代理。得知這個消息的我激動壞了。這麼好!我要去看看。

到了之後我就跟他們老闆說,我太難了,我的事情都想交給你代理啊,當然了錢不是問題,我的花唄借唄你想還多少就還多少。然後老闆就感動的啊!一把鼻涕一把淚的。然後擦完鼻涕擦着嘴跟我說:小夥啊 ,既然你不缺錢的話,我就把我們公司的套餐給你說說啊,【這個厲害啊 這個代理所還有套餐】我不缺的是債,我缺錢,你說吧什麼套餐。老闆接着說

尊貴專享套餐:JDK動態代理 套餐

豪華定製套餐:cglib動態代理 套餐

我一聽難爲壞了,我一個選擇困難症,你讓我選套餐。你先給我介紹一下,我回去想想。

動態代理(一個代理類可以代理任何類型)【原來動態代理這麼方便啊,是我要找的】

優點: 動態代理與靜態代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。而且動態代理的應用使我們的類職責更加單一,複用性更強

缺點:沒有靜態代理的效率高,爲每個被代理類都重新再內存中加載了一個新的類,並沒有用自己寫的被代理類

聽到這我都睡着了,然後就回了家

管他什麼套餐呢 我當然選最好最划算的了

然後我就自己琢磨起來

一: jdk動態代理

1. 定義一個需求的接口

package dynamicproxy.jdkdynamicproxy;

// 我的所有需求
public interface Needs {
    // 喫飽的需求
    void eat();
    // 結婚的需求
    void marry();
}

2. 定義一個被代理者 【我把代碼寫的緊湊了一點可以一鍵格式化代碼看着就方便了,IDEA的快捷鍵Ctrl+Alt+L】

package dynamicproxy.jdkdynamicproxy;

// 這是需求多的我 要實現所有的需求接口
public class Me implements Needs {
    private String name = "小星星";
    private String age = "18";
    @Override
    public void eat() { System.out.println("我要喫世界上最好喫的飯"); }
    @Override
    public void marry() {
        System.out.println("我叫" + this.getName() + "我有小脾氣");
        System.out.println("我" + this.getAge() + "歲,要找一個膚白貌美大長腿的女朋友");
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getAge() { return age; }
    public void setAge(String age) { this.age = age; }
}

3. 定義一個代理者

package dynamicproxy.jdkdynamicproxy;

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

// 一條龍代理所的 尊貴專享套餐:JDK動態代理
// 要用這個套餐就要按JDK動態代理規定的做 實現它的InvocationHandler接口
public class ADragonAgency implements InvocationHandler {
    // 一條龍代理所需要知道每個使用套餐的客戶需求
    private Me target;

    // 每個客戶去辦理套餐的時候都要提供自己的信息
    public Object getInstance(Me target){
        this.target = target;
        Class clazz = Me.class;
        // 接受三個參數 1.被代理類的classLoader 2. 被代理類的實現接口 3. 代理類 即指定使用哪個代理類來處理
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        System.out.println("尊貴專享套餐的顧客你好,你的需求如下:");
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("您的需求我們這就去完成");
        return obj;
    }
}

4. 定義一個調用者 通過代理類來實現我的需求

package dynamicproxy.jdkdynamicproxy;

// 調用者
public class Client {
    public static void main(String[] args) {
        Needs n = (Needs) new ADragonAgency().getInstance(new Me());
        n.eat();
        // n.marry();
    }
}

以上的代碼看起來似乎是沒毛病了,如果我有了新的需求我直接再接口中添加一個新的 然後在被代理類中實現 如果代理流程不變的話 我就不用修改代理類 最後直接在調用者中調用就好了,

但是我突然想到一個問題 這個代理類總不能爲我一個人(被代類Me服務吧) 現在只能給ADragonAgency().getInstance() 方法傳Me的類 ,如果有其他人也同樣有Needs這樣的需求 這就還要寫一個同樣的代理類 好像也麻煩啊,

接下來就解決這個麻煩只需要做如下修改

然後就可以給ADragonAgency().getInstance() 方法傳任何實現Needs接口的方法

這樣看起來這個尊貴專享套餐還挺好,那我們繼續瞭解一下另一個套餐吧

二:cglib動態代理

1. 定義一個被代理類

// 定義我的需求 不需要寫接口來實現
public class Me {
    public String name = "小星星";
    public String age = "18";
    public void eat() { System.out.println("我要喫世界上最好喫的飯"); }
    public void marry() {
        System.out.println("我叫" + this.getName() + "我有小脾氣");
        System.out.println("我" + this.getAge() + "歲,要找一個膚白貌美大長腿的女朋友");
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getAge() { return age; }
    public void setAge(String age) { this.age = age; }
}

2. 定義一個代理類

package dynamicproxy.cglibdynamicproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ADragonAgency implements MethodInterceptor {

    // 每個客戶去辦理套餐的時候都要提供自己的信息
    public Object getInstance(Object obj){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("一條龍代理所很榮幸爲您服務");
        // 注意這裏是 invokeSuper 寫錯成 invoke 會死循環
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("您的需求我們這就去完成");
        return obj;
    }
}

3. 定義一個使用者

package dynamicproxy.cglibdynamicproxy;

public class Client {
    public static void main(String[] args) {
        Me me = (Me)new ADragonAgency().getInstance(new Me());
        me.eat();
        me.marry();
    }
}

總結:

動態代理實際就是做了字節碼重組

1. 學到了什麼

較清楚的學習了 java的反射機制

對於代理模式深入的學習

jdk動態代理源碼XX學習

cglib動態代理源碼XX學習

什麼時候用什麼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章