本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
今天测试一下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();
}
可以看到正确的执行了接口中的实现方法
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);
}
}
也没有任何问题,正确的执行了这个类中的方法。
那么我们再看下这两种代理方式性能上的差别
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);
}
可以看到,毫无疑问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来进行修改