jdk动态代理和cglib代理到底谁快

410 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

今天测试一下jdk动态代理和cglib代理到底谁快

首先我们要明白jdk代理和cglib代理的区别

  • jdk代理是利用反射生成一个实现代理类接口的匿名类,调用方法前调用InvokeHandler方法来处理。注意重点jdk动态代理是代理接口。
  • cglib是为那些没有接口的类来创建对象。cglib是代理类。

如何在spring框架中使用cglib代理

spring.aop.proxy-target-class=true

接下来我们看看jdk动态代理和cglib代理的实现方式和性能上的差别。

jdk动态代理

首先创建一个接口和实现类

public interface TestService {

   String test();
}

@Service
public class TestServiceImpl implements TestService {

   @Override
   public String test() {
      String str = "test";
      return str;
   }
}

InvocationHandler接口的实现

public class JdkProxy implements InvocationHandler {

    /** 目标代理对象 */
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(target, args);
        return result;
    }
}
    public static void main(String[] args) {
      TestService testService = new TestServiceImpl();
      // 通过构造方法的方式创建目标代理对象
      JdkProxy jdkProxy = new JdkProxy(testService);
      TestService service = (TestService) Proxy.newProxyInstance(testService.getClass().getClassLoader(), testService.getClass().getInterfaces(), jdkProxy);
      String test = service.test();

   }

可以看到正确的执行了接口中的实现方法 微信图片_20220115232440.png

cglib代理

一个类并没有实现任何接口

public class TestServiceImp2{

   public String test() {
      String str = "test cglibProxy";
      return str;
   }
}
public class CglibProxy implements MethodInterceptor {

    /** 目标代理对象 */
    private Object target;

    public Object create(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return methodProxy.invokeSuper(o, objects);
    }
}

也没有任何问题,正确的执行了这个类中的方法。

微信图片_20220115232807.png

那么我们再看下这两种代理方式性能上的差别

public static void main(String[] args) {
   TestService testService = new TestServiceImpl();
   long jdkStart = System.currentTimeMillis();
   JdkProxy jdkProxy = new JdkProxy(testService);
   TestService service = (TestService) Proxy.newProxyInstance(testService.getClass().getClassLoader(), testService.getClass().getInterfaces(), jdkProxy);
   String test = service.test();
   System.out.println(test);
   System.out.println(System.currentTimeMillis() - jdkStart);

   TestServiceImp2 serviceImpl = new TestServiceImp2();
   long cglibStart = System.currentTimeMillis();
   CglibProxy cglibProxy = new CglibProxy();
   TestServiceImp2 impl = (TestServiceImp2)cglibProxy.create(serviceImpl);
   String test1 = impl.test();
   System.out.println(test1);
   System.out.println(System.currentTimeMillis() - cglibStart);
}

微信图片_20220115233043.png

微信图片_20220115233047.png

可以看到,毫无疑问jdk动态代理快。那么这是为什么呢?

  • CGLib底层采用ASM字节码生成框架,使用字节码技术生成被代理类的一个代理子类。CGLib不能对声明为final的方法进行代理
  • 在jdk1.8之前jdk动态代理并没有cglib快,在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率。而在jdk1.8的时候版本升级,性能明显高于cglib代理

spring框架什么情况下使用cglib代理

  • Spring 中的 AOP,有接口就用 JDK 动态代理,没有接口就用 Cglib 动态代理
  • Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理
  • SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB
  • 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改