JDK动态代理和CGLib

动态代理介绍

应用场景

假设现在已经存在一个children接口,其中一个方法是eat()你家有个小孩myChild类,你希望他讲卫生懂礼貌,以后成为新世纪的四好少年,所以要让他在吃饭前洗手,为了能够有时间打王者荣耀需要让他在吃饭后就睡觉。那这个时候的第一想法可能在children类中eat()方法的前后增加washHands和sleep的操作,或者增加方法washHand()和sleep(),然后在eat()方法中引用。这两种方法都可以解决这个问题,但是开闭原则告诉我们-对修改关闭╮(╯▽╰)╭。这样就堵死了这两种简单的方式。在你一筹莫展的时候,动态代理就站在了你的面前,拯救你于水火之中。

原理

我们在解决问题的时候往往想的就是简单直接的解决当前面对的问题,但是现实的情况往往不能如愿,既然不能够直接修改myChild类,那么就只能在你家小孩吃的之前增加一道洗手的程序,然后还是让你的小孩吃饭,吃饭之后再增加一道睡觉的程序。这个时候就相当于在吃饭这个操作的前后增加了不同的操作,但是对于吃饭本身来说并没有任何影响。

分类

动态代理分为两种:JDK提供的动态代理和第三方的CGLib

JDK动态代理示例

JDK动态代理简介

JDK动态代理顾名思义是由JDK提供的一种动态代理方式,简介结束。

类图

类图1.png

具体代码

撒话不说直接上代码

接口Children

public interface Children {
    public void eat();
}

实现类MyChild

public class MyChild implements Children {
    @Override
    public void eat() {
        System.out.println("eat Something");
    }
}

处理类DynamicChildren

public class DynamicChildren implements InvocationHandler {
    private Object children;
    public DynamicChildren(Object object){
        this.children = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Wash Hands!");
//        调用实际实例的eat方法
        method.invoke(children, args);
        System.out.println("Sleep!");
        return null;
    }
}

客户端Client

public class Client {
    public static void main(String[] args) {

        MyChild myChild = new MyChild();
        InvocationHandler handler = new DynamicChildren(myChild);
        Class<?> classType = handler.getClass();
        /*classType.getClassLoader():获得DynamicChildren类加载器
        myChild.getClass().getInterfaces():获得MyChild实现的所有接口类,当前的MyChild只实现了Children一类接口,所以这获得的只有这个
        handler:DynamicChildren
        children即为生成的动态代理类*/
        Children children = (Children) Proxy.newProxyInstance(classType.getClassLoader(), myChild.getClass().getInterfaces(), handler);

//        这实际上是调用的DynamicChildren中的invoke方法
        children.eat();
    }
}

小结

以上就是我理解的JDK提供的动态代理相关的东西,完美是相对的,JDK动态代理也是一样有优缺点的。

优点

  • 不依赖第三方jar包, 使用方便
  • 随着JDK的升级,JDK动态代理的性能在稳步提升

缺点

  • 只适用于实现了接口类
  • 执行速度较慢

看了以上的内容,你的心中一定会冒出一个疑问,如果没有实现接口的类想要动态代理,是不是需要另外一种代理方式了呢?你猜的没错!这个时候就到了另一种动态代理-CGLibの出番だ!

timg.jpg

CGLib简介

CGLIB(Code Generation Library)是一个开源项目!
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate> 支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

以上摘自百度百科

CGLib的原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截当前类的所有父类方法的调用,顺势织入横切逻辑。

使用的前置条件

需要在项目中依赖cglib相关的jar包和asm jar包。我使用的是cglib-2.2.jar、cglib-nodep-2.2.jar和asm-3.2.jar。

类图

类图2.jpg

具体代码

撒话不说直接上代码

需要代理的类MyChild

public class MyChild {
    public void eat() {
        System.out.println("eat something");
    }
}

处理类CglibProxy

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Class clazz) {
//        设置需要创建动态代理类的类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
//        通过字节码动态创建实例
        return enhancer.create();
    }

    /**
     * 实现 MethodInterceptor接口方法
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
        System.out.println("Wash Hands!");
//        通过代理类调用父类中的方法
        Object result = proxy.invokeSuper(obj, arg);
        System.out.println("Sleep!");
        return result;
    }
}

客户端类DoCGLib

public class DoCGLib {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
//        通过生成子类的方式来生成代理类
        MyChild myChild = (MyChild) proxy.getProxy(MyChild.class);
        myChild.eat();
    }
}

小结

以上就是我理解的CGLib动态代理相关的东西,JDK动态代理有优缺点,那么CGLib动态代理也不能少。

优点

  • 可以代理没有实现接口的对象
  • 由于是动态生成字节码实现代理,因此代理对象的执行速度较快, 约为JDK动态代理的1.5 ~ 2倍

缺点

  • 不能代理final类
  • 动态生成字节码虽然执行较快,但是生成速度很慢,根据网上一些人的测试结果,CGLib创建代理对象的速度要比JDK慢10 ~ 15倍。

JDK动态代理和CGLib适用场景

  • 由于JDK动态代理在创建代理对象上比CGLib快,所以如果你的程序需要频繁、反复地创建代理对象,请将JDK动态代理定位为备胎一号。
  • 由于CGLib的执行速度比JDK快,所以如果不需要频繁创建代理对象的应用,如spring中默认的单例bean,只需要在容器启动时生成一次代理对象这种情况,请优先考虑转正CGLib。

以上です!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容