設計模式學習——代理模式

ManBa-java_Kobe發表於2020-10-01



基本介紹

  1. 代理模式:為一個物件提供一個替身,以控制對這個物件的訪問,即通過代理物件訪問目標物件.這樣做的好處是:可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能
  2. 被代理的物件可以是遠端物件建立開銷大的物件需要安全控制的物件
  3. 代理模式有不同的形式、主要有三種靜態代理動態代理(JDK代理、介面代理)Cglib代理(可以再記憶體動態的建立物件,而不需要實現介面,它是屬於動態代理點額範疇)

靜態代理

靜態代理的基本介紹

靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同父類

靜態代理類圖例項

在這裡插入圖片描述
說明:

  1. 定義一個介面ITeacherDao
  2. 目標物件TeacherDao實現介面ITeacherDao
  3. 使用靜態代理方式就需要在代理物件TeacherDaoProxy中也實現ITeacherDao介面
  4. 呼叫的時候通過呼叫代理物件的方法來呼叫目標物件。
  5. 特別提醒:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來呼叫目標物件的方法。

靜態代理程式碼例項

public interface ITeacherDao {
    void teach(); //授課的方法
}

被代理物件

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老師授課中....");
    }
}

代理物件

//代理物件
public class TeacherDaoProxy implements ITeacherDao {

    private ITeacherDao target;//目標物件,通過介面來聚合


    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("開始代理,完成某些操作......");
        target.teach();
        System.out.println("代理結束");
    }
}

測試用例

public class Client {
    public static void main(String[] args) {
        //建立目標物件(被代理物件)
        TeacherDao teacherDao = new TeacherDao();
        //建立代理物件,同時將被代理物件傳遞給代理物件
        TeacherDaoProxy teacherDaoProxy = new          TeacherDaoProxy(teacherDao);
        //通過代理物件,呼叫到被代理物件的方法
        //即:執行的是代理物件的方法,代理物件再去呼叫目標物件的方法。
        teacherDaoProxy.teach();
    }
}

靜態代理優缺點

  1. 優點:在不修改目標物件的功能前提下,能通過代理物件對目標功能擴充套件
  2. 缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類
  3. 一旦介面增加方法,目標物件與代理物件都要維護。

動態代理

動態代理模式基本介紹

  1. 代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
  2. 代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件。
  3. 動態代理也叫做:JDK代理、介面代理。

JDK生成代理物件的API

  1. 代理類所在包:java.lang.reflect.Proxy
  2. JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個引數

動態代理類圖例項

在這裡插入圖片描述

動態代理程式碼例項

public interface ITeacherDao {

    void teach();

    void sayHello(String name);
}

被代理物件

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老師授課中.....");
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello" + name);
    }
}

代理工廠

public class ProxyFactory {

    //維護一個目標物件,Object
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //給目標物件生成一個代理物件
    public Object getProxyInstance(){

        /**
         * public static Object newProxyInstance(ClassLoader loader,
         *                                           Class<?>[] interfaces,
         *                                           InvocationHandler h)
         * 引數說明:
         *   1. ClassLoader:指定當前目標物件使用的類載入器,獲取載入器的方法固定
         *   2. Class<?>[] interfaces:目標物件實現的介面型別,使用泛型的方式確認型別
         *   3. InvocationHandler h:事件處理,執行目標物件的方法時,會觸發事件處理器
         *   的方法,會把當前執行的目標物件方法作為引數傳入。
         */
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("JDK代理開始~");
                        //反射機制呼叫目標物件的方法
                        Object returnVal = method.invoke(target, args);
                        System.out.println("JDK代理提交");
                        return returnVal;
                    }
                });
    }
}

測試用例

public class Client {

    public static void main(String[] args) {
        //建立目標物件
        ITeacherDao target = new TeacherDao();
        //給目標物件建立代理物件,可以轉為ITeacherDao
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

        //列印資訊:proxyInstanceclass com.sun.proxy.$Proxy0
        //說明:記憶體中動態生成了代理物件
        System.out.println("proxyInstance" + proxyInstance.getClass());

        //通過代理物件呼叫目標物件得方法。
        proxyInstance.teach();

        proxyInstance.sayHello("Tom");
    }
}

Cglib代理

Cglib代理模式得基本介紹

  1. 靜態代理和JDK代理模式都要求目標物件是實現一個介面,但是有時候目標物件只是一個單獨得物件,並沒有實現任何得介面,這個時候可使用目標物件子類來實現代理-這就是Cglib代理。
  2. Cglib代理也叫作子類代理,它是在記憶體中構建一個子類物件從而實現對目標物件功能擴充套件,有些書也將Cglib代理歸屬到動態代理。
  3. Cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件java類與實現java介面.它廣泛的被許多AOP的框架使用,例如Spring AOP,實現方法攔截。
  4. 在AOP程式設計中如何選擇代理模式:
    1 > 目標物件需要實現介面,用JDK代理
    2 > 目標物件不需要實現介面,用Cglib代理
  5. Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類

Cglib代理模式類圖例項

在這裡插入圖片描述
說明:

  1. 給目標物件target建立一個代理物件
  2. 重寫intercept方法,實現對被代理物件(目標物件)方法呼叫

Cglib代理模式程式碼例項

被代理物件

public class TeacherDao {
    public void teach() {
        System.out.println("老師授課中,我是Cglib代理,不需要實現介面");
    }
}

代理工廠

public class ProxyFactory implements MethodInterceptor {

    //維護一個目標物件
    private Object target;

    //構造器,傳入一個被代理的物件
    public ProxyFactory(Object target) {
        this.target = target;
    }

    //返回一個代理物件: 是target 物件的代理物件
    public Object getProxyInstance() {

        //1. 建立一個工具類
        Enhancer enhancer = new Enhancer();
        //2. 設定父類
        enhancer.setSuperclass(target.getClass());
        //3. 設定回撥函式
        enhancer.setCallback(this);
        //4. 建立子類物件,即代理物件
        return enhancer.create();
    }

    //重寫intercept方法,會呼叫目標物件的方法
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib代理模式~~ 開始");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式~~ 提交");
        return returnVal;
    }
}

測試用例

public class Client {

    public static void main(String[] args) {
        //建立目標物件
        TeacherDao target = new TeacherDao();
        //獲取到代理物件,並且將目標物件傳遞給代理物件
        TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(target).getProxyInstance();
        //執行代理物件的方法,出發intercept方法,從而實現對目標物件的呼叫
        proxyInstance.teach();
    }
}

代理模式(Proxy)的變體

  1. 防火牆代理
    內網通過代理穿透防火牆,實現對公網的訪問。
  2. 快取代理
    比如,當請求圖片檔案等資源時,先到快取代理取,如果取到資源則OK,如果取不到資源,再到公網或者資料庫取,然後快取。
  3. 遠端代理
    遠端物件的本地代表,通過它可以把遠端物件當本地物件來呼叫,遠端代理通過網路和真正的遠端物件溝通訊息。
  4. 同步代理
    主要使用再多執行緒程式設計中,完成多執行緒間同步工作

相關文章