設計模式知識點整理-代理模式(靜態代理,jdk動態代理,cglib動態代理)

什麼是代理模式

代理模式是一種結構型設計模式,簡單解釋就是使用代理對象調用目標對象,並且在不改變源碼的情況下,實現對目標對象的功能擴展。

代理類中的兩個角色

目標類

代理類

舉個例子,有個藝術表演者,他的核心任務是表演才藝

public class Performer{

     public void perform(){

         System.out.println("表演才藝");

     } 

}

他在表演才藝之前,先跟觀衆打招呼,表演完才藝之後,向觀衆致謝

public void perform(){  

        System.out.println("各位觀衆朋友們大家好");

        System.out.println("表演才藝");

        System.out.println("感謝大家的觀看");

}

核心任務是表演才藝,非核心任務是打招呼跟致謝,我們想把這兩項分開。表演者類設計爲目標類,只保留表演才藝的核心任務。再設計一個代理類,實現對目標類的調用與擴展,代理類中實現打招呼跟致謝的功能,這就是我們用到的代理模式。

分類

代理模式分爲靜態代理跟動態代理,動態代理又分爲jdk動態代理跟cglib動態代理

靜態代理

使用靜態代理重構之前的例子

表演接口

public interface IPerform {

    public void perform();

}

表演者目標類

public class Performer implements IPerform {

    @Override

    public void perform() {

        System.out.println("表演才藝");

    }

}

表演者代理類

public class PerformProxy implements IPerform {

    //接收目標對象

    private  IPerform target;

    public PerformProxy(IPerform perform) {

        this.target = perform;

    }


    @Override

    public void perform() {

        //表演前先向觀衆們問好

        System.out.println("各位觀衆朋友們大家好");

        //調用目標方法

        target.perform();

        //表演完成向觀衆致謝

        System.out.println("感謝大家的觀看");

    }

}

測試

public class Test {

    public static void main(String[] args) {

        //目標對象

        IPerform perform = new Performer();

        //代理對象,將目標對象注入

        IPerform performProxy = new PerformProxy(perform);


        performProxy.perform();

    }

}

測試結果

各位觀衆朋友們大家好

表演才藝

感謝大家的觀看

靜態代理總結

靜態代理中,將目標類注入到代理類中,實現在代理類中調用目標類的方法,並擴展目標類的功能。但是代理對象需要與目標對象實現一樣的接口,代理對象必須提前寫出,如果接口層發生了變化,代理對象的代碼也要進行維護。如果能在運行時動態地寫出代理對象,不但減少了一大批代理類的代碼,也少了不斷維護的煩惱。這種方式就是接下來的動態代理

jdk動態代理

jdk動態代理代理類所在的包爲:java.lang.reflect.Proxy,調用Proxy類的靜態方法newProxyInstance,該方法會返回代理類對象。

newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )接收的三個參數依次爲:

ClassLoader loader:指定當前目標對象使用類加載器,寫法固定

Class<?>[] interfaces:目標對象實現的接口的類型,寫法固定

InvocationHandler h:事件處理接口,需傳入一個實現類,一般直接使用匿名內部類

java底層封裝了實現細節

使用jdk動態代理重構之前的例子

表演接口

public interface IPerform {

    public void perform();

}

表演者目標類

public class Performer implements IPerform {

    @Override

    public void perform() {

        System.out.println("表演才藝");

    }

}

表演者代理類

public class PerformProxy {

    //接收目標對象

    private  IPerform target;

    public PerformProxy(IPerform perform) {

        this.target = perform;

    }


    //使用jdk動態代理,爲目標對象生成代理對象

    public Object getProxyInstance(){

        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("各位觀衆朋友們大家好");

                        //調用目標方法

                        Object returnValue = method.invoke(target, args);

                        //表演完成向觀衆致謝

                        System.out.println("感謝大家的觀看");

                        return returnValue;

                    }

                });

    }

}

測試

public class Test {

    public static void main(String[] args) {

        //目標對象

        IPerform perform = new Performer();

        //代理對象,將目標對象注入

        PerformProxy performProxy = new PerformProxy(perform);

        IPerform proxy = (IPerform)performProxy.getProxyInstance();

        //通過代理對象調用目標對象方法

        proxy.perform();


    }

}

jdk動態代理總結

jdk動態代理中的目標對象必須實現接口,否則無法使用該代理方式。如果目標對象沒有實現接口,我們可以使用cglib動態代理。

cglig動態代理

前提條件:

需要引入cglib的jar文件與asm的jar文件,cglib依賴於asm。由於Spring的核心包中已經包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar

目標類不能爲final

目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法

使用jdk動態代理重構之前的例子

表演者目標類

public class Performer {

    public void perform() {

        System.out.println("表演才藝");

    }

}

表演者代理類

public class PerformProxy implements MethodInterceptor {

    //接收目標對象

    private  Object  target;

    public PerformProxy(Object  perform) {

        this.target = perform;

    }


    //獲取目標對象的代理對象

    public Object getProxyInstance(){

        //工具類

        Enhancer en = new Enhancer();

        //設置父類對象

        en.setSuperclass(this.target.getClass());

        //設置回調函數

        en.setCallback(this);

        //創建子類,也就是代理對象

        return en.create();

    }


    @Override

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        //表演前先向觀衆們問好

        System.out.println("各位觀衆朋友們大家好");

        //執行目標對象的方法

        Object returnValue = method.invoke(target, objects);

        //表演完成向觀衆致謝

        System.out.println("感謝大家的觀看");

        return returnValue;

    }

}

測試

public class Test {

    public static void main(String[] args) {

        //目標對象

        Performer perform = new Performer();

        PerformProxy performProxy = new PerformProxy(perform);

        //生成代理對象

        Performer proxy = (Performer)performProxy.getProxyInstance();

        proxy.perform();

    }

}

在Spring的AOP編程中: 如果加入容器的目標對象有實現接口,用JDK代理 如果目標對象沒有實現接口,用Cglib代理

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