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。若是有不足的地方,请小伙伴们不吝指教。某必将洗耳恭听

相关文章
相关标签/搜索