5、代理模式

代理模式

定義

爲其餘對象提供一種代理以控制對這個對象的訪問。java

爲何要用代理模式?

  • 中介隔離做用:在某些狀況下,一個客戶類不想或者不能直接引用一個委託對象,而代理類對象能夠在客戶類和委託對象之間起到中介的做用,其特徵是代理類和委託類實現相同的接口。
  • 開閉原則,增長功能:代理類除了是客戶類和委託類的中介以外,咱們還能夠經過給代理類增長額外的功能來擴展委託類的功能,這樣作咱們只須要修改代理類而不須要再修改委託類,符合代碼設計的開閉原則。代理類主要負責爲委託類預處理消息、過濾消息、把消息轉發給委託類,以及過後對返回結果的處理等。代理類自己並不真正實現服務,而是同過調用委託類的相關方法,來提供特定的服務。真正的業務功能仍是由委託類來實現,可是能夠在業務功能執行的先後加入一些公共的服務。例如咱們想給項目加入緩存、日誌這些功能,咱們就可使用代理類來完成,而不必打開已經封裝好的委託類。

有哪幾種代理模式?

按照代理建立的時期來進行分類的話, 能夠分爲兩種:靜態代理、動態代理。靜態代理是由程序員建立或特定工具自動生成源代碼,在對其編譯。在程序員運行以前,代理類.class文件就已經被建立了。動態代理是在程序運行時經過反射機制動態建立的。程序員

靜態代理

靜態代理在使用時,須要定義接口或者父類,被代理對象與代理對象一塊兒實現相同的接口或者是繼承相同父類。緩存

實現工具

這裏假設Tom要買一套房子,而後本身沒有足夠的資金,因而就讓他的父親代理他來給他買下這套房子。性能

買房子接口:測試

public interface BuyHouse {
    void Buy();
}

Tom類:this

public class Tom implements BuyHouse {
    public void Buy() {
        System.out.println("Tom買到房子了...");
    }
}

Father類:設計

public class Father implements BuyHouse {
    private Tom tom;

    public Father(Tom tom){
        this.tom = tom;
    }
    
    public void Buy() {
        System.out.println("Father給Tom買了房子...");
        tom.Buy();
    }
}

靜態代理的優缺點:代理

  • 優勢:在不修改目標對象功能的前提下,能經過代理對象對目標功能進行擴展
  • 缺點:由於代理對象要與目標對象實現同樣的接口,因此會有不少代理類;一旦接口方法增長,目標對象與代理對象都須要維護。

動態代理日誌

  • 代理對象不須要實現接口,可是目標對象仍然須要實現接口,不然不能使用動態代理
  • 代理對象的生成,是利用JDK的API,動態的內從中構建代理對象

下面咱們使用JDK動態代理的方式來對上面的靜態代理示例進行改寫

買房子接口和Tom類都和上面的同樣

建立代理類JdkProxy:

public class JdkProxy implements InvocationHandler {
    private Object target;
    //接收被代理的目標對象對象
    public JdkProxy(Object target){
        this.target = target;
    }
    //生成目標對象的代理對象
    public Object getProxyInstance(){
        Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return instance;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("開始Jdk動態代理,來給Tom買房子");
        //執行目標對象的方法
        Object returnVal = method.invoke(target,args);
        return returnVal;
    }
}

測試類:

public class testJdkProxy {
    @Test
    public void  test(){
        BuyHouse tom = new Tom();
        JdkProxy jdkProxy = new JdkProxy(tom);
        BuyHouse proxyInstance = (BuyHouse)jdkProxy.getProxyInstance();
        proxyInstance.Buy();
    }
}

/**測試結果
開始Jdk動態代理,來給Tom買房子
Tom買到房子了...
*/

注意Proxy.newProxyInstance()方法接受三個參數:

  • ClassLoader loader:指定當前目標對象使用的類加載器,獲取加載器的方法是固定的
  • Class<?>[] interfaces:指定目標對象實現的接口的類型,使用泛型方式確認類型
  • InvocationHandler:指定動態處理器,執行目標對象的方法時,會觸發事件處理器的方法

Cglib代理

靜態代理和JDK代理模式都要求目標對象實現一個接口,可是有時候目標對象只是一個單獨的對象,並無實現任何對象,這個時候可使用目標對象的子類來實現代理,這就是Cglib代理。

CGLib採用了很是底層的字節碼技術,其原理是經過字節碼技術爲一個類建立子類,並在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。但由於採用的是繼承,因此不能對final修飾的類進行代理。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。

實現

建立沒有實現接口的Tom類:

public class Tom {
    public void Buy() {
        System.out.println("Tom買到房子了...");
    }
}

建立Cglib代理類:

public class CglibProxy implements MethodInterceptor {
    private Object target;

    public Object getProxyInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("開始Cglib代理,來給Tom買房子");
        Object returnVal = method.invoke(target, objects);
        return returnVal;
    }
}

建立測試類:

public class testCglibProxy {
    @Test
    public void test(){
        Tom proxyInstance = (Tom) new CglibProxy().getProxyInstance(new Tom());
        proxyInstance.Buy();
    }
}

/**測試結果
開始Cglib代理,來給Tom買房子
Tom買到房子了...
*/

CGLIB代理總結: CGLIB建立的動態代理對象比JDK建立的動態代理對象的性能更高,可是CGLIB建立代理對象時所花費的時間卻比JDK多得多。因此對於單例的對象,由於無需頻繁建立對象,用CGLIB合適,反之使用JDK方式要更爲合適一些。同時因爲CGLib因爲是採用動態建立子類的方法,對於final修飾的方法沒法進行代理。

相關文章
相關標籤/搜索