1. 程式人生 > >設計模式-代理模式(Proxy)

設計模式-代理模式(Proxy)

代理模式(Proxy)

  • 定義 : 為其他物件提供一種代理, 以控制對這個物件的訪問
  • 代理物件在客戶端和目標物件之間起到中介的作用
  • 型別 : 結構型

適用場景

  • 保護目標物件
  • 增強目標物件

優點

  • 代理模式能將代理物件與真實被呼叫的目標物件分離
  • 一定程度上降低了系統的耦合度, 擴充套件性好
  • 保護目標物件
  • 增強目標物件

缺點

  • 造成系統設計中類的數目增加
  • 在客戶端和目標物件之間增加了一個代理物件, 會造成一定的效能下降
  • 增加了系統的複雜度

擴充套件

  • 靜態代理
  • 動態代理 : 只能代理實現了介面的類
  • CGLib代理 : 通過繼承實現的, 所以要注意final關鍵字

Spring代理選擇

  • 當Bean中有實現介面時, Spring就會用JDK的動態代理

  • 當Bean中沒有實現介面時, Spring使用CGLib

  • 也可以強制使用CGLib, 增加如下配置即可:

    <aop : aspectj-autoproxy proxy-target-class="true"/>
    

模式角色

  • Proxy :

    • 儲存一個引用使得代理可以訪問實體。若 RealSubject和Subject的介面相同,Proxy會
      引用Subject。
    • 提供一個與Subject的介面相同的介面,這樣代理就可以用來替代實體。
    • 控制對實體的存取,並可能負責建立和刪除它。
    • 控制對實體的存取,並可能負責建立和刪除它。
  • Subject : 定義RealSubject 和Proxy 的共用介面,這樣就在任何使用 RealSubject的地方都可以使
    用Proxy。

  • RealSubject : 定義Proxy所代表的實體

程式碼實現

靜態代理

UML類圖:
在這裡插入圖片描述

對應各模組程式碼:

/**
 * @author 七夜雪
 * @create 2018-11-24 9:31
 */
public interface Subject { public void showName(); }
/**
 * 被代理類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:30
 */
public class RealSubject implements Subject {

    @Override
    public void showName() {
        System.out.println("被代理類方法---");
    }
}
/**
 * 代理類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:32
 */
public class Proxy implements Subject {
    private RealSubject realSubject = new RealSubject();

    @Override
    public void showName() {
        System.out.println("被代理類方法執行之前執行...");
        realSubject.showName();
        System.out.println("被代理類方法執行之後執行...");
    }
}

測試類 :

/**
 * 測試類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:34
 */
public class Client {

    public static void main(String[] args) {
        Subject subject = new Proxy();
        subject.showName();
    }
}

測試結果 :

被代理類方法執行之前執行...
被代理類方法---
被代理類方法執行之後執行...

動態代理

這裡就以JDK的動態代理為例, 要實現JDK的動態代理, 就需要實現InvocationHandler介面, 下面是具體程式碼實現:
動態代理類 :

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

/**
 * 動態代理
 *
 * @author 七夜雪
 * @create 2018-11-24 9:55
 */
public class DynamicProxy implements InvocationHandler {
    private Object target;

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

    public Object bind(){
        Class cls = target.getClass();
        return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
    }

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

    private void beforeMethod(){
        System.out.println("動態代理 before code");
    }

    private void afterMethod(){
        System.out.println("動態代理 after code");
    }
}

Subject介面 :

/**
 * @author 七夜雪
 * @create 2018-11-24 9:31
 */
public interface Subject {
    public void showName();

}

Subject介面的兩個實現類, 即被代理的類 :

/**
 * 被代理類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:30
 */
public class RealSubject1 implements Subject {

    @Override
    public void showName() {
        System.out.println("被代理類方法1---");
    }
}
/**
 * 被代理類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:30
 */
public class RealSubject1 implements Subject {

    @Override
    public void showName() {
        System.out.println("被代理類方法2---");
    }
}

測試程式碼 :

/**
 * 測試類
 *
 * @author 七夜雪
 * @create 2018-11-24 9:34
 */
public class Client {

    public static void main(String[] args) {
        Subject proxy1 = (Subject) new DynamicProxy(new RealSubject1()).bind();
        Subject proxy2 = (Subject) new DynamicProxy(new RealSubject2()).bind();
        proxy1.showName();
        proxy2.showName();
    }
}

測試結果 :

動態代理 before code
被代理類方法1---
動態代理 after code
動態代理 before code
被代理類方法2---
動態代理 after code

本文參考:
慕課網<java設計模式精講 Debug 方式+記憶體分析>課程
四人幫<設計模式>