Cglib動態代理基礎使用

小弟因爲前段時間突兀的理解了jdk 的動態代理,爲了更方便研究spring 的AOP源碼,就試着理解了一下cglib動態代理的基礎:html

一、若是針對接口作代理默認使用的是JDK自帶的Proxy+InvocationHandlerjava

二、若是針對類作代理使用的是Cglibspring

三、即能針對接口作代理,也能夠將代理方式配置成走Cglib的數組

下面就記錄一下 對類作代理ide

package com.wisely.cglibproxy;

/**
 * DES:cglib 原生類
 * Created by Reynole-白
 * Date: 2017/9/4 15:03
 */
public class Dao {

    
    public void update() {
        System.out.println("i am update method!!!!");
    }

    public void select() {
        System.out.println("i am select method!!!!");
    }

    public void delete(){
        System.out.println("i am delete method!!!");
    }

}

上面這個類沒有什麼特別的地方,很普通工具

接下來建立一個Dao的代理類工具 DaoProxy測試

package com.wisely.cglibproxy;

import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
import org.assertj.core.internal.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * DES: dao 類的cglib代理類,這裏使用的cglib 是org.assertj.core.internal.cglib.proxy包下的
 * Created by Reynole-白
 * Date: 2017/9/4 15:06
 */
public class DaoProxy implements MethodInterceptor{

    /**
     *
     * @param object  要進行加強的對象 就是代理對象
     * @param method  表示攔截的方法
     * @param objects 數組表示參數列表,基本數據類型須要傳入其包裝類型,如int-->Integer、long-Long、double-->Double
     * @param methodProxy 對方法的代理,invokeSuper方法表示對被代理對象方法的調用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//        System.out.println(object.getClass().getName());
        System.out.println("代理方法執行以前");
        methodProxy.invokeSuper(object, objects);
        System.out.println("代理方法執行完成後");

        return object;
    }
}

該類實現了MethodInterceptor對象,並重寫了intercept方法該方法有四個參數,代碼中有說明特別說明,最後一個參數是方法的代理對象,用於執行原生方法的。代理

來一個測試類code

package com.wisely.cglibproxy;

import org.assertj.core.internal.cglib.proxy.Callback;
import org.assertj.core.internal.cglib.proxy.Enhancer;
import org.assertj.core.internal.cglib.proxy.NoOp;
import org.junit.Test;

/**
 * DES: cglib 測試類
 * Created by Reynole-白
 * Date: 2017/9/4 15:16
 */
public class CglibTest {

    
    @Test
    public void testCglib2() {
        DaoProxy daoProxy = new DaoProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);//設置要代理的對象
        enhancer.setCallback(daoProxy);//回調 哪一個代理對象

        Dao dao = (Dao)enhancer.create();//建立代理對象
        dao.update();
        dao.select();
    }

}

運行結果:htm


代理方法執行以前
i am update method!!!!
代理方法執行完成後
代理方法執行以前
i am select method!!!!
代理方法執行完成後

Process finished with exit code 0

我特地查詢了一些資料。顯示,cglib代理廣泛用於對方法的攔截上面。其重寫的方法名稱叫intercept也說明了其用途。

-------------------------------------------------------------------------------------------------------------

下面擴展一下,對不一樣的原生方法作不一樣的代理或者說是攔截。

新定義一個代理對象,與以前的implements同樣

package com.wisely.cglibproxy;

import org.assertj.core.internal.cglib.proxy.MethodInterceptor;
import org.assertj.core.internal.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * DES: 對不一樣方法進行不一樣策略的攔截,使用cglib實現
 * Created by Reynole-白
 * Date: 2017/9/4 15:31
 */
public class DaoAnotherProxy implements MethodInterceptor {


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

        System.out.println("StartTime=[" + System.currentTimeMillis() + "]");
        methodProxy.invokeSuper(o, objects);
        System.out.println("EndTime=[" + System.currentTimeMillis() + "]");
        return o;

    }
}

爲了對不一樣的方法實現不一樣的攔截,須要定義一個Filter

package com.wisely.cglibproxy;

import org.assertj.core.internal.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

/**
 * DES: 定義實現回調來接的類
 * Created by Reynole-白
 * Date: 2017/9/4 15:33
 */
public class DaoFilter implements CallbackFilter{

    /**
     * 攔截到指定方法後返回對應數字
     * @param method
     * @return
     */
    @Override
    public int accept(Method method) {
        if ("select".equals(method.getName())) {
            return 0;
        }else if("update".equals(method.getName())){
            return 1;
        }

        return 2;
    }
}

該Filter主要用於在代理對象調用不一樣方法時實現不一樣的攔截邏輯

測試類:

package com.wisely.cglibproxy;

import org.assertj.core.internal.cglib.proxy.Callback;
import org.assertj.core.internal.cglib.proxy.Enhancer;
import org.assertj.core.internal.cglib.proxy.NoOp;
import org.junit.Test;

/**
 * DES: cglib 測試類
 * Created by Reynole-白
 * Date: 2017/9/4 15:16
 */
public class CglibTest {

    @Test
    public void testCglib() {
        DaoProxy daoProxy = new DaoProxy();
        DaoAnotherProxy daoAnotherProxy = new DaoAnotherProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);
        enhancer.setCallbacks(new Callback[]{daoProxy,daoAnotherProxy, NoOp.INSTANCE});
        /**
         * setCallbackFilter的值,是規定cglib代理的攔截順序是按照DaoFilter執行,
         * 與setCallbacks中的代理對象順序一致
         * DaoFilter 的accept方法返回的整數,就是調用Callback 代理對象的索引
         *
         * NoOp.INSTANCE 表示空Callback ,DaoFilter返回2時調用空代理不作任何攔截處理,
         * 直接調用原生方法
         */
        

        Dao dao = (Dao)enhancer.create();//建立代理對象
        dao.update();
        dao.select();
        dao.delete();
    }

    @Test
    public void testCglib2() {
        DaoProxy daoProxy = new DaoProxy();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dao.class);//設置要代理的對象
        enhancer.setCallback(daoProxy);//回調 哪一個代理對象

        Dao dao = (Dao)enhancer.create();//建立代理對象返回 指定原生對象但執行是,會先執行代理對象的方法
        dao.update();
        dao.select();
    }

}

Enhancer對象能夠設置代理對象 攔截方法後要處理什麼邏輯,直接將DaoFilter賦值進去便可,根據其Filter返回的整數,執行setCallbacks中的哪一個代理對象。

好比執行update方法的時候,返回是1,那麼執行的就是setCallbacks中索引是1的daoAnotherProxy代理對象,若是返回是2,那麼就執行NoOp.INSTANCE(空代理),NoOp.INSTANCE 表明空代理,直接指定原生方法,不作任何攔截處理。若是自定義的Filter中返回的整數在Callbacks 代理數組中沒有對應索引,就會跑出異常:java.lang.IllegalArgumentException: Callback filter returned an index that is too large: X

執行結果:
StartTime=[1504514792310]
i am update method!!!!
EndTime=[1504514792324]
代理方法執行以前
i am select method!!!!
代理方法執行完成後
i am delete method!!!

==============================================================

其實還有一種場景。若是Dao有一個構造器相似這種:

package com.wisely.cglibproxy;

/**
 * DES:cglib 原生類
 * Created by Reynole-白
 * Date: 2017/9/4 15:03
 */
public class Dao {

    public Dao(){
        update();
    }

    public void update() {
        System.out.println("i am update method!!!!");
    }

    public void select() {
        System.out.println("i am select method!!!!");
    }

    public void delete(){
        System.out.println("i am delete method!!!");
    }

}

在cglib代理攔截時,也是會執行攔截操做。這樣輸出就會冗餘。怎麼設置能避免這種狀況呢?

Enhancer對象能夠設置對構造器不作攔截處理的方法:

enhancer.setInterceptDuringConstruction(false);//取消代理對象對構造器內方法的攔截控制

加上這句,就能夠了。有興趣的小夥伴能夠測試一下

 

以上課題和實例均參考http://www.cnblogs.com/xrq730/p/6661692.html。若是有不足的地方,請小夥伴們不吝指教。某必將洗耳恭聽

相關文章
相關標籤/搜索