JAVAJAVA

JDK动态代理和CGLib

2017-04-18  本文已影响507人  NekoJiang

动态代理介绍

应用场景

假设现在已经存在一个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动态代理也是一样有优缺点的。

优点

缺点

看了以上的内容,你的心中一定会冒出一个疑问,如果没有实现接口的类想要动态代理,是不是需要另外一种代理方式了呢?你猜的没错!这个时候就到了另一种动态代理-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动态代理和CGLib适用场景

以上です!

上一篇下一篇

猜你喜欢

热点阅读