動態代理知識回顧

一、動態代理的特點

字節碼隨用隨創建,隨用隨加載。
它與靜態代理的區別也在於此,因爲靜態代理是字節碼一上來就創建好,並完成加載
裝飾者模式就是靜態代理的一種體現。
(1) 基於接口的動態代理:
提供者: JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。

(2)基於子類的動態代理
提供者:第三方的 CGLib,如果報 asmxxxx 異常,需要導入 asm.jar
要求:被代理類不能用 final 修飾的類(最終類)。

二、基於接口的動態代理

案例:在很久以前,演員和劇組都是直接見面聯繫的。沒有中間人環節。而隨着時間的推移,產生了一個新興職業:經紀人(中間人),這個時候劇組再想找演員就需要通過經紀人來找了。下面我們就用代碼演示出來。

創建代理對象:使用Proxy類中的newProxyInstance方法
創建代理對象的要求:被代理類最少實現一個接口,如果沒有則不能使用

newProxyInstance方法的參數:
   * ClassLoader 類加載器:它是用於加載代理對象字節碼的,和被代理對象使用相同的類加載器,固定寫法。
   * Class[] 字節碼數組:它是用於讓代理對象和被代理對象有相同方法,固定寫法。
   * InvocationHandler:用於提供增強的代碼
     * 它是讓我們寫如何代理。我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
     * 此接口的實現類都是誰用誰寫。

(1)演員類

/**
 * 一個演員
 * 實現了接口,就表示具有接口中的方法實現。即:符合經紀公司的要求
 */
public class Actor implements IActor {
    @Override
    public void basicAct(float money) {
        System.out.println("拿到錢,開始基本的表演:" + money);
    }

    @Override
    public void dangerAct(float money) {
        System.out.println("拿到錢,開始危險的表演:" + money);
    }
}

(2)經紀公司接口

/**
 * 一個經紀公司的要求:
 *      能做基本的表演和危險的表演
 */
public interface IActor {
    /**
     * 基本演出
     * @param money
     */
    public void basicAct(float money);
    /**
     * 危險演出
     * @param money
     */
    public void dangerAct(float money);
}

(3)模擬消費者

public class Client {
    public static void main(String[] args) {
        //一個劇組直接找演員
        final Actor actor = new Actor();
        //創建代理對象的要求:被代理類最少實現一個接口,如果沒有則不能使用
        IActor proxyActor = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
                actor.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 執行被代理對象的任何方法,都會經過該方法。
                     * 此方法有攔截的功能。
                     *
                     * 參數:
                     * proxy:代理對象的引用。不一定每次都用得到
                     * method:當前執行的方法對象
                     * args:執行方法所需的參數
                     * 返回值:
                     * 當前執行方法的返回值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        String name = method.getName();
                        Float money = (Float) args[0];
                        Object rtValue = null;
                        if ("dangerAct".equals(name)) {
                            //危險演出,沒有5000不演
                            if (money > 5000) {
                                //劇組扣除一半,演員到手一半,這裏實際對方法進行了增強
                                rtValue = method.invoke(actor, money / 2);
                            }
                        }
                        return rtValue;
                    }
                });
        //1.沒有經紀公司,直接找演員
        actor.dangerAct(5000f);
        //2.通過經紀公司聯繫演員
        proxyActor.dangerAct(50000f);
    }
}

三、基於子類的動態代理

涉及的類:Enhancer
提供者:第三方cglib庫

<!--實現基於子類的動態代理的cglib庫-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.1_3</version>
</dependency>
如何創建代理對象:
    * 使用Enhancer類中的create方法
    * 創建代理對象的要求:被代理類不能是最終類
    * create方法的參數:
    * Class:字節碼,它是用於指定被代理對象的字節碼。
	* Callback:用於提供增強的代碼,它是讓我們寫如何代理。
		* 我們一般都是些一個該接口的實現類,通常情況下都是匿名內部類,但不是必須的。
        * 此接口的實現類都是誰用誰寫。
        * 我們一般寫的都是該接口的子接口實現類:MethodInterceptor

(1)Actor類不需要接口

/**
 * 一個演員:此處未實現任何接口
 */
public class Actor {
    public void basicAct(float money) {
        System.out.println("拿到錢,開始基本的表演: " + money);
    }

    public void dangerAct(float money) {
        System.out.println("拿到錢,開始危險的表演: " + money);
    }
}

(2)

public class Client {
    public static void main(String[] args) {
        final Actor actor = new Actor();
        /**
         * 基於子類的動態代理
         * 要求:被代理對象不能是最終類
         * 用到的類:Enhancer
         * 用到的方法:create(Class, Callback)
         * 方法的參數:
             * Class:被代理對象的字節碼
             * Callback:如何代理
         * @param args
         */
        Actor cglibActor = (Actor) Enhancer.create(actor.getClass(),
                new MethodInterceptor() {
                    /**
                     * 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何方法進行增強。
                     * 參數:
                        * 前三個和基於接口的動態代理是一樣的。
                        * MethodProxy:當前執行方法的代理對象。
                     * 返回值:當前執行方法的返回值
                     */
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        String name = method.getName();
                        Float money = (Float) objects[0];
                        Object rtValue = null;
                        if("dangerAct".equals(name)){
                            //危險演出
                            if(money > 5000){
                                rtValue = method.invoke(actor, money/2);
                            }
                        }
                        return rtValue;
                    }
                });
        cglibActor.dangerAct(100000);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章