博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Cglib动态代理基础使用
阅读量:6478 次
发布时间:2019-06-23

本文共 6586 字,大约阅读时间需要 21 分钟。

hot3.png

小弟由于前段时间突兀的理解了jdk 的动态代理,为了更方便研究spring 的AOP源码,就试着理解了一下cglib动态代理的基础:

1、如果针对接口做代理默认使用的是JDK自带的Proxy+InvocationHandler

2、如果针对类做代理使用的是Cglib

3、即能针对接口做代理,也可以将代理方式配置成走Cglib的

下面就记录一下 对类做代理

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方法该方法有四个参数,代码中有说明特别说明,最后一个参数是方法的代理对象,用于执行原生方法的。

来一个测试类

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();    }}

运行结果:

代理方法执行之前
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。如果有不足的地方,请小伙伴们不吝指教。某必将洗耳恭听

转载于:https://my.oschina.net/u/2543341/blog/1528743

你可能感兴趣的文章
-27979 LoadRunner 错误27979 找不到请求表单 Action.c(73): Error -27979: Requested form not found...
查看>>
[LeetCode] Minimum Depth of Binary Tree
查看>>
,net运行框架
查看>>
Java 中 Emoji 的正则表达式
查看>>
Mixin Network第一届开发者大赛作品介绍- dodice, diceos和Fox.one luckycoin
查看>>
安卓Glide(4.7.1)使用笔记 01 - 引入项目
查看>>
中金易云:为出版社找到下一本《解忧杂货店》
查看>>
Flex布局
查看>>
Material Design之 AppbarLayout 开发实践总结
查看>>
Flutter之MaterialApp使用详解
查看>>
DataBinding最全使用说明
查看>>
原生Js交互之DSBridge
查看>>
Matlab编程之——卷积神经网络CNN代码解析
查看>>
三篇文章了解 TiDB 技术内幕 —— 说计算
查看>>
copy strong weak assign的区别
查看>>
OpenCV 入门
查看>>
css 3D transform变换
查看>>
ele表格合并行之后的selection选中
查看>>
正则表达式分解剖析(一文悟透正则表达式)
查看>>
解决UILable标点符号居中的问题
查看>>