Java結構型設計模式 —— 必須要瞭解的代理模式

 

一、引言

一年四季,這今年上海的冬天格外的冷

童鞋,聽說你還不夠了解代理模式,來看這篇文章就夠了,本文看起來通俗易懂,讀起來朗朗上口,寫撰水平絕不亞於莎士比亞的四大悲劇。

不知道扯些什麼啦,進入主題把~~~~~~~~~~~

二、代理模式基本介紹

代理模式小夥伴多多少少都有了解,就是通過一個代理對象,去調用另外一個對象的方法是吧?

下面是一段白話:

代理模式:爲一個對象提供了一個替身,已控制對這個對象的訪問。即通過代理對象訪問目標對象,這樣做的好處是:可以在目標對象實現的基礎上,增加額外的功能操作(比如:添加日誌,校驗信息),即擴展目標對象的功能。

靜態代理:實現比較簡單,但不夠靈活。

動態代理:也可以叫做JDK代理、接口代理,需要實現InvocationHandler接口。

Cglib代理:它屬於動態代理的範疇,可以動態的創建對象,而不需要實現接口。需要實現MethodInterceptor接口

以上只是對代理模式做了一個大概的簡介,那麼接下來逐步對每一中代理模式進行學習吧。

需求:老師上課之前需要進行點名,老師下課需要完成打卡。

三、靜態代理

靜態代理的實現是非常簡單的,需要定義一個接口或者父類並相對應的實現或者繼承,代理類也要一起實現相同的接口或者繼承相同的父類。

靜態代理可以讓在不修改目標對象的功能前提下,能通過代理對象對目標對象進行擴展,但一旦接口增加方法,目標對象與代理對象都要維護。

特別提醒:代理對象與目標對象要實現相同的接口,然後通過調用相同的方法來調用目標對象的方法。

步驟一:定義老師上課的接口,這裏可以是接口或者抽象類

/**
 * @Auther: IT賤男
 * @Date: 2019/9/11 14:57
 * @Description: 教師相關行爲定義
 */
public interface ITeacherService {

    /**
     * 老師上課
     */
    void teachClass();

}

步驟二:老師上課接口實現類,實現具體上課流程。 (代理的目標類)

/**
 * @Auther: IT賤男
 * @Date: 2019/9/11 14:57
 * @Description: 目標對象
 */
public class TeacherServiceImpl implements ITeacherService {
    
    @Override
    public void teachClass() {
        System.out.println("老師正在上課");
    }
}

步驟三:創建代理類,也要實現相同的接口,並且聚合目標類。 

/**
 * @Auther: IT賤男
 * @Date: 2019/9/11 14:58
 * @Description: 代理對象
 */
public class TeacherServiceProxy implements ITeacherService {
    
    // 聚合目標類
    private ITeacherService teacherService;

    public TeacherServiceProxy(ITeacherService teacherService) {
        this.teacherService = teacherService;
    }

    /**
     * 代理類同樣的實現了老師上課的方法,但是最終還是調用的目標類
     * 在調用目標類方法之前以及之後,都可以做相對應的操作,這就是靜態代理
     */
    @Override
    public void teachClass() {
        System.out.println("上課前:老師進行同學點名");
        teacherService.teachClass();
        System.out.println("上完課:老師進行課時打卡");
    }
}

步驟四:測試,通過代理對象,在原本老師上課的方法上,添加了新的功能,符合開閉原則。

/**
 * @Auther: IT賤男
 * @Date: 2019/9/11 14:59
 * @Description: 測試
 */
public class Client {

    public static void main(String[] args) {
        // 獲取代理對象
        TeacherServiceProxy proxy = new TeacherServiceProxy(new TeacherServiceImpl());
        // 調用代理方法
        proxy.teachClass();
    }

}

四、動態代理

動態代理和靜態代理最大的區別就在於,動態代理類不需要實現相同的接口或者繼承相同的父類,但目標類還是需要實現的。

使用動態代理,不管目標類是否新增了方法,對代理類都不會產生任何影響,可維護性比較好。代理對象的生產,是利用JDK的API,動態的在內存中構建代理對象

但我們動態需要實現java.lang.reflect.Proxy這個把包中InvocationHandler這個接口,調用Proxy.newProxyInstance方法來創建一個代理對象。

其他代碼和靜態代理一樣,小編就只展示動態代理類。

/**
 * @Auther: IT賤男
 * @Date: 2019/9/12 11:25
 * @Description: 動態代理實現
 */
public class TeacherServiceProxy implements InvocationHandler {

    // 目標對象
    private Object targer;

    public Object getProxyInstance(Object targer) {
        // 給目標對象賦值
        this.targer = targer;
        // 創建代理對象
        // targer.getClass().getClassLoader() 用於創建動態代理的ClassLoader對象
        // targer.getClass().getInterfaces() 接口數組,這裏的接口包含 ITeacherService ,就是定義老師行爲的接口
        // this,最後代理所處理的類,因爲本類實現類InvocationHandler的invoke方法,所以直接傳this
        return Proxy.newProxyInstance(targer.getClass().getClassLoader(), targer.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("上課前:老師進行同學點名");
        Object invoke = method.invoke(targer, args);
        System.out.println("上完課:老師進行課時打卡");
        return invoke;
    }
}
/**
 * @Auther: IT賤男
 * @Date: 2019/9/12 11:28
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        // 創建獲取代理對象
        TeacherServiceProxy factory = new TeacherServiceProxy();
        ITeacherService proxyInstance = (ITeacherService) factory.getProxyInstance(new TeacherServiceImpl());
        // 調用代理對象的方法
        proxyInstance.teachClass();
    }
}

五、Cglib 代理

 不管是靜態代理還是動態代理他們始終都需要依賴一個接口或者父類,但Cglib代理則可以拋開這種束縛。Cglib代理可以在內存中動態創建對象,而不需要實現接口,它屬於動態代理的範疇。

Cglib代理也叫做子類代理,他是在內存中構建一個子類對象從而實現對目標對象功能擴展。

你看,這是一個類,沒有繼承和實現任何類或者接口,直接實現了老師上課的方法。

/**
 * @Auther: IT賤男
 * @Date: 2019/9/11 14:57
 * @Description: 教師相關行爲方法
 */
public class Teacher {

    /**
     * 老師上課
     */
    public void teachClass() {
        System.out.println("老師正在上課 。。。。 我是Cglib代理對象");
    }

}
/**
 * @Auther: IT賤男
 * @Date: 2019/9/12 10:37
 * @Description: Cglib 代理對象
 */
public class TeacherProxy implements MethodInterceptor {

    // 目標對象
    private Object targer;

    // 通過構造器傳入一個被代理的對象
    public TeacherProxy(Object targer) {
        this.targer = targer;
    }

    // 返回一個代理對象,是targer對象的代理對象
    public Object getProxyInstance() {
        // 創建一個工具類
        Enhancer enhancer = new Enhancer();
        // 設置父類
        enhancer.setSuperclass(targer.getClass());
        // 設置回調函數
        enhancer.setCallback(this);
        // 創建代理對象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("上課前:老師進行同學點名");
        Object invoke = method.invoke(targer, objects);
        System.out.println("上完課:老師進行課時打卡");
        return invoke;
    }
}
/**
 * @Auther: IT賤男
 * @Date: 2019/9/12 10:42
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        // 獲取代理對象
        TeacherProxy factory = new TeacherProxy(new Teacher());
        Teacher teacher = (Teacher) factory.getProxyInstance();

        // 執行代理對象的方法,觸發intercept方法,從而實現對目標對象的調用
        teacher.teachClass();
    }
}

六、最最最最後總結一下

不管是靜態代理、動態代理、Cglib代理,它們都是爲一個對象提供一個替身,來控制對這個對象的訪問。就是通過代理類來訪問目標類,這樣做的好處就是可以在目標對象的基礎之上,增強額外的功能操作,即擴展了目標對象。

靜態代理:實現比較簡單,目標類和代理類都需要實現相同的方法或者父類,維護比較麻煩。

動態代理:代理對象需要實現InvocationHandler接口,動態代理將目標對象和代理對象進行了解耦。

Cglib代理:代理對象需要實現MethodInterceptor接口,在目標對象沒有實現或者繼承任何接口或者類但情況下,則考慮可以選擇cglib代理。

 

以上就是小編對代理模式的一個總結,如有疑問可以留言評論。

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