JDK动态代理和CGLIB动态代理

121 阅读2分钟

之前写过一篇关于JDK动态代理分析。juejin.cn/post/685736…

今天想聊一下JDK动态代理和CGLIB动态代理区别

JDK动态代理

public class MyProxy<T> implements InvocationHandler {

    /**
     * 被代理的对象
     */
    private T t;

    public Object getProxyObj(T t){
        this.t = t;
        return Proxy.newProxyInstance(t.getClass().getClassLoader(),
                t.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("begin");
        method.invoke(t,args);
        return null;
    }
}

CGLI动态代理

public class MyMethodInterceptor implements MethodInterceptor {

    public Object getInstance(Class clazz) {
        // 在指定目录下生成动态代理类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        // 设置目标类的字节码文件
        enhancer.setSuperclass(clazz);
        // 设置回调
        enhancer.setCallback(this);
        // 创建代理类
        // 第一步、生成源代码
        // 第二步、编译成class文件
        // 第三步、加载到JVM中,并返回被代理对象
        return enhancer.create();
    }

    /**
     * obj:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======begin======");
        Object object = methodProxy.invokeSuper(obj, objects);
        System.out.println("======end======");
        return object;
    }
}

JDK动态代理代理
1)实现InvocationHandler接口,重写invoke()
2)使用Proxy.newProxyInstance()产生代理对象
3)被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
1)实现MethodInterceptor接口,重写intercept()
2)使用Enhancer对象.create()产生代理对象

测试

public class Test {

    public static void main(String[] args) throws Exception {
        MyProxy mp = new MyProxy();
        IUserService u = (IUserService)mp.getProxyObj(new UserServiceImpl());
        u.addUser();

        MyMethodInterceptor myt = new MyMethodInterceptor();
        Dog proxyDog = (Dog)myt.getInstance(Dog.class);
        proxyDog.eat();

    }
}

通过2段代码

IUserService u = (IUserService)mp.getProxyObj(new UserServiceImpl());

Dog proxyDog = (Dog)myt.getInstance(Dog.class);

     我们发现JDK的动态代理是基于接口的,要求被代理的类要实现一个接口而CGLIB的动态代理是基于类的,它不要求被代理的类实现接口,通过派生出子类实现代理。并对父类方法进行增强,因此被代理的类不能有final关键字所修饰

     这里的动态是针对静态代理而言的,静态代理是在编译阶段就确定的,二动态代理在运行阶段才确定,这样就大大的增加了程序的灵活性。使用过Spring的人都知道,AOP就是动态代理的运用。

      不管是JDK的动态代理还是CGLIB的动态代理都是字节码技术,通过操作字节码,从而生成代理对象。

      提到字节码大家应该很熟悉吧,Java是跨平台语言,之所以跨平台是因为每个平台都有自己的jvm,jvm是用来执行字节码解释工作。一个.Java源文件经过编译后成.class文件.class文件可以被不同平台的jvm解释执行。.java文件经过javac命令变成.class文件,javac命令就是字节码技术,CGLIB是第三方字节码类库

    强制使用CGLIB实现AOP的方法
1)添加CGLIB库
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>