JDK与Cglib动态代理区别与原理

前言

针对如题的区别问题,大多数同学第一回答基本都是JDK的动态代理需要被代理类实现接口,而Cglib动态代理是无须这个要求,由于继承所以无法代码初final修饰的类。

一、嵌套方法

public interface UserService {
    String name();
    String forName();
}

public class UserServiceImpl implements UserService {
    @Override
    public String name() {
        System.out.println("invoke name");
        return "hello,world";
    }
    @Override
    public String forName() {
        System.out.println("invoke forName");
        return name() + ",bye.";
    }
}

如上代码所示,在forName的方法中又调用了name方法。

在JDK的代码实现:

    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserService service = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("abc");
                Object invoke = method.invoke(userService, args);
//                Object invoke = method.invoke(proxy, args);
                System.out.println("efg");
                return invoke;
            }
        });
    }

从打印结果分析,只有forName被代理,而name方法的调用并没有被代理。

CgLib代理

public class MyMethodInterceptor implements MethodInterceptor {
    private Object target;

    public MyMethodInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        //CgLib是继承,所以需要调用父类方法
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}
public class CglibProxyTest {
        public static void main(String[] args) {
            // 代理类class文件存入本地磁盘方便我们反编译查看源码
            //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
            // 通过CGLIB动态代理获取代理对象的过程
            Enhancer enhancer = new Enhancer();
            // 设置enhancer对象的父类
            enhancer.setSuperclass(UserServiceImpl.class);
            // 设置enhancer的回调对象
            enhancer.setCallback(new MyMethodInterceptor(null));
            // 创建代理对象
            UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
            // 通过代理对象调用目标方法
            proxy.forName();
        }
}

通过打印的方法,可以看到forName和name都并代理执行了。

二、JDK动态代理原理分析

  • JDK动态代理是实现被代理类的接口,代理对象可以理解成与被代理对象是兄弟关系
  • Cglib动态代理是继承被代理类,代理对象可以理解成与被代理对象是父子关系

JDK动态代理对象,通过Debug可以确认
在这里插入图片描述
类名是形如$Proxy开头的名称,通过代码

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{UserService.class});
        FileOutputStream fos = new FileOutputStream("F:/$Proxy0.class");
        fos.write(bytes);
        fos.flush();
        fos.close();

将内存中的类字节码输出到文件,并反编译,可以分析通过JDK动态代理生成的类结构。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import com.zte.sdn.oscp.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String name() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String forName() throws  {
        try {
            return (String)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.zte.sdn.oscp.service.UserService").getMethod("name");
            m4 = Class.forName("com.zte.sdn.oscp.service.UserService").getMethod("forName");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

首先观察接口类中方法的实现

(String)super.h.invoke(this, m3, (Object[])null);

h变量即InvocationHandler,m3变量为通过反射获取的方法

m3 = Class.forName(“com.zte.sdn.oscp.service.UserService”).getMethod(“name”);

此外equals和hashCode以及toString方法都会被统代理

所以JDK的动态代理原理则十分清晰,通过继承接口,重写方法,回调InvocationHandler逻辑

那JDK为何不能嵌套调用原因也很简单,forName---->InvocationHandler------->原始对象的name方法

三、CgLib动态代理原理分析

通过如下代码,可以将CgLib生成的继承类输出

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “F:\”);

生成代码有:
在这里插入图片描述
类定义如下,继承

public class UserServiceImpl$$EnhancerByCGLIB$$2eb8ebd2 extends UserServiceImpl implements Factory {

继承类中重写了方法forName和name

    public final String forName() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$forName$1$Method, CGLIB$emptyArgs, CGLIB$forName$1$Proxy) : super.forName();
    }

一样地,也是传入的回调即intercept,但需要注意的是此时传入的对象this本身就是代理对象,那为什么他不会产生递归调用呢?原因即在于他针对方法提供了一个MethodProxy#invokerSuper,通过这个封装可以控制只调用父类的方法。

因此CGLIB生成的类为原始类的子类,第一次经过的方法会打印一次==插入前置通知==,然后调用原对象方法,如果这个方法里又嵌套了方法,这时,子类也复写了这个方法,所以仍然通过代理对象调用,所以会再走一次intercept,即再打印==插入前置通知==

模拟代码如下,在forName里强制调用父类的方法,调用name时回调子类的重写的name

public class ChildUserServiceImpl extends UserServiceImpl {

    @Override
    public String name() {
        return "ChildUserName";
    }

    @Override
    public String forName() {
        return super.forName();
    }

    public static void main(String[] args) {
        ChildUserServiceImpl service = new ChildUserServiceImpl();
        String s = service.forName();
        System.out.println(s);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值