Spring之代理模式

爲什麼要用代理模式?
  • 中介隔離作用:
    在某些情況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象可以在客戶類和委託對象之間起到中介的作用,其特徵是代理類和委託類實現相同的接口。

  • 開閉原則,增加功能:
    代理類除了是客戶類和委託類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類,符合代碼設計的開閉原則。代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後對返回結果的處理等。代理類本身並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能還是由委託類來實現,但是可以在業務功能執行的前後加入一些公共的服務。例如我們想給項目加入緩存、日誌這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委託類.

代理模式:

        代理模式作爲23種經典設計模式之一,其比較官方的定義爲“爲其他對象提供一種代理以控制對這個對象的訪問”,簡單點說就是,之前A類自己做一件事,在使用代理之後,A類不直接去做,而是由A類的代理類B來去做

1.靜態代理

        特點是代理對象是針對指定的目標做的, 所有代碼都固定的, 由程序員提供. 代碼實現非常簡單, 不能複用.

靜態代理代碼如下:

/**
 * 標準 - 房屋租賃接口
 */
public interface Rent {
    /**
     * 出租房屋的方法
     *
     * @param money
     * @return
     */
    Object rent(Object money);
}
/**
 * 真實對象 - 房東
 */
public class Host implements Rent {
    @Override
    public Object rent(Object money) {
        System.out.println("出租成功, 價格是: " + money);
        return new Object();
    }
}
/**
 * 代理對象 - 中介人員
 */
public class Agent implements Rent {
    private Rent host;

    public Rent getHost() {
        return host;
    }

    public void setHost(Rent host) {
        this.host = host;
    }

    @Override
    public Object rent(Object money) {
        // 前置增強
        System.out.println("帶客戶看房");
        System.out.println("講價");
        System.out.println("簽訂合同");
        // 調用業主的方法進行房屋的出租
        Object key = host.rent(money);
        // 後置增強
        System.out.println("客戶信息登記...");
        System.out.println("客戶維護...");
        return key;
    }
}
/**
 * 客戶 - 租客
 */
public class Customer {
    public static void main(String[] args) {
        // 找中介
        Agent proxy = new Agent();
        // 中介找業主
        proxy.setHost(new Host());
        // 租房
        Object key = proxy.rent(2000);
    }
}

2.動態代理

        特點是代理對象不是固定的, 代碼是由後臺根據情況生成的. 代理功能更加通用. 代碼實現複雜, 可以複用.

動態代理的實現方式有兩種:
2.1 jdk動態代理

jdk中自帶的動態代理實現方案. Proxy類和InvocationHandler接口.

  • 生成的代理對象都是Proxy類的子類.
  • 動態代理中額外增強的功能, 被稱之爲調用處理程序. 必須實現InvocationHandler接口.
  • 必須提供接口.
  • 生成的代理類和真實類是實現了相同接口的. 它倆不能互相轉換, 否則會拋出ClassCastException.

    代碼如下:
/**
 * 標準 - 接口
 */
public interface Rent {
    /**
     * 租房方法
     *
     * @param money
     * @return
     */
    Object rent(Object money);
}
/**
 * 真實對象 - 業主
 */
public class Host implements Rent {
    @Override
    public Object rent(Object money) {
        System.out.println("出租成功, 價格: " + money);
        return new Object();
    }
}
/**
 * 中介公司 - 調用處理程序
 */
public class Agent implements InvocationHandler {
    private Rent host;

    public Rent getHost() {
        return host;
    }

    public void setHost(Rent host) {
        this.host = host;
    }

    /**
     * 將來被自動調用
     *  1. 最終需要調用業主的方法把房子租出去
     *  2. 對這個功能要進行增強
     * @param proxy 代理對象
     * @param method 正在被調用的方法
     * @param args 被調用方法的參數列表
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增強
        System.out.println("籤合同...");
        // 調用真實對象的方法
        Object result = method.invoke(host, args);
        // 後置增強
        System.out.println("客戶登記...");
        return result;
    }

    /**
     * 獲取代理對象
     *
     * @return
     */
    public Rent getProxy() {
        return (Rent) Proxy.newProxyInstance(
                Agent.class.getClassLoader(),
                new Class<?>[]{Rent.class},
                this);
    }
}
public class Customer {
    public static void main(String[] args) {
        // 中介公司
        Agent agent = new Agent();
        // 中介公司找業主
        agent.setHost(new Host());
        // 從中介公司獲取代理對象
        Rent proxy = agent.getProxy();
        System.out.println(proxy.getClass().getName());
        System.out.println(proxy.getClass().getSuperclass().getName());
        // 調用代理對象的租房方法
        proxy.rent(3000);
    }
}
2.2 cglib動態代理
  • 需要額外導包, cglib相關的jar包: cglib.jar, asm.jar
  • 不需要提供接口, 代理類是真實類的子類.
  • 調用處理程序需要實現MethodInterceptor接口.
/**
 * 真實對象 - 業主
 */
public class Host {
    public Object rent(Object money) {
        System.out.println("房屋出租, 價格: " + money);
        return new Object();
    }
}
/**
 * 中介公司 - 調用處理程序
 */
public class Agent implements MethodInterceptor {
    /**
     * 方法攔截
     *  1. 調用真實類中的方法
     *  2. 需要對真實類中的方法進行增強
     *
     * @param obj 代理對象
     * @param method 被調用的方法
     * @param args 方法的參數列表
     * @param methodProxy 被代理的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 前置增強
        System.out.println("[前置]");
        // 調用真實類中的方法
        Object result = methodProxy.invokeSuper(obj, args);
        // 後置增強
        System.out.println("[後置]");
        return result;
    }

    public Host getProxy() {
        // 創建增強子Enhancer
        Enhancer enhancer = new Enhancer();
        // 設置父類型
        enhancer.setSuperclass(Host.class);
        // 設置回調
        enhancer.setCallback(this);
        // 創建代理對象
        return (Host) enhancer.create();
    }
}
public class Customer {
    public static void main(String[] args) {
        // 找中介
        Agent agent = new Agent();
        // 獲取代理對象
        Host proxy = agent.getProxy();
        System.out.println(proxy.getClass().getName());
        System.out.println(proxy.getClass().getSuperclass().getName());
        // 調用方法進行租房
        proxy.rent(500);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章