JDK、CGLIB、Javassist和ASM的动态代理使用对比
动态代理是指在运行时,动态生成代理类。正如标题所示,能够提供动态代理的方式有很多。既然选择这么有很多,那么如何选择会更好呢? 带着这个疑问,我找到了Dubbo的作者——梁飞大神的一篇文章。文章中对四种方式都做了性能测试,从当时测试的结果来看,Javassist成了最好的选择。 不过时间过了那么久,现在在JDK 1.8上,根据上面的测试用例测试,会发现JDK动态代理和CGLIB动态代理的性能提升了很多。 测试的版本信息如下: 测试结果如下: Create JDK Proxy: 4 ms Create CGLIB Proxy: 108 ms Create JAVAASSIST Proxy: 39 ms Create JAVAASSIST Bytecode Proxy: 82 ms Create ASM Proxy: 4 ms ================ Run JDK Proxy: 136 ms,367,647,000 t/s Run CGLIB Proxy: 215 ms,232,558,000 t/s Run JAVAASSIST Proxy: 1690 ms,29,585,000 t/s Run JAVAASSIST Bytecode Proxy: 183 ms,273,224,000 t/s Run ASM Bytecode Proxy: 172 ms,290,697,000 t/s ---------------- Run JDK Proxy: 203 ms,246,305,000 t/s Run CGLIB Proxy: 288 ms,173,611,000 t/s Run JAVAASSIST Proxy: 1457 ms,34,317,000 t/s Run ASM Bytecode Proxy: 188 ms,265,957,000 t/s ---------------- Run JDK Proxy: 288 ms,000 t/s Run CGLIB Proxy: 272 ms,183,823,000 t/s Run JAVAASSIST Proxy: 1469 ms,036,000 t/s Run JAVAASSIST Bytecode Proxy: 188 ms,000 t/s Run ASM Bytecode Proxy: 179 ms,279,329,000 t/s ---------------- 从测试结果来看,JDK代理和CGLIB代理性能已经有很大的提升,ASM与Javassist在性能上已经拉不开差距了。 在易用性方面:
在代理对象的创建速度上,JDK代理与ASM都很快,比最慢的CGLIB快20倍以上。 结论从上面的分析结果来看,性能上各种方式的差距不算太大。 考虑到易用性,在对接口进行动态代理时,使用JDK代理应该是最合适的。 在不能使用JDK代理的情况下,可以考虑使用CGLIB或者Javassist。 CGLIB的缺点是创建代理对象的速度慢,Javassist的缺点是需要手动编写Java源码。 如果非要在这个两个中选择一个,那么只有在对性能要求非常高的情况下选择Javassist,其他一般情况下,个人认为CGLIB是比较合适的。 测试代码由于现在的机器运行速度较快,为了对比明显,我将原先1千万的数据计算,改成了5千万。 package fun.example.netty.proxy; import javassist.*; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.FieldVisitor; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.internal.org.objectweb.asm.Opcodes; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.text.DecimalFormat; public class DynamicProxyPerformanceTest { public static void main(String[] args) throws Exception { CountService delegate = new CountServiceImpl(); long time = System.currentTimeMillis(); CountService jdkProxy = createJdkDynamicProxy(delegate); time = System.currentTimeMillis() - time; System.out.println("Create JDK Proxy: " + time + " ms"); time = System.currentTimeMillis(); CountService cglibProxy = createCglibDynamicProxy(delegate); time = System.currentTimeMillis() - time; System.out.println("Create CGLIB Proxy: " + time + " ms"); time = System.currentTimeMillis(); CountService javassistProxy = createJavassistDynamicProxy(delegate); time = System.currentTimeMillis() - time; System.out.println("Create JAVAASSIST Proxy: " + time + " ms"); time = System.currentTimeMillis(); CountService javassistBytecodeProxy = createJavassistBytecodeDynamicProxy(delegate); time = System.currentTimeMillis() - time; System.out.println("Create JAVAASSIST Bytecode Proxy: " + time + " ms"); time = System.currentTimeMillis(); CountService asmBytecodeProxy = createAsmBytecodeDynamicProxy(delegate); time = System.currentTimeMillis() - time; System.out.println("Create ASM Proxy: " + time + " ms"); System.out.println("================"); for (int i = 0; i < 3; i++) { test(jdkProxy,"Run JDK Proxy: "); test(cglibProxy,"Run CGLIB Proxy: "); test(javassistProxy,"Run JAVAASSIST Proxy: "); test(javassistBytecodeProxy,"Run JAVAASSIST Bytecode Proxy: "); test(asmBytecodeProxy,"Run ASM Bytecode Proxy: "); System.out.println("----------------"); } } private static void test(CountService service,String label) throws Exception { service.count(); // warm up int count = 50000000; long time = System.currentTimeMillis(); for (int i = 0; i < count; i++) { service.count(); } time = System.currentTimeMillis() - time; System.out.println(label + time + " ms," + new DecimalFormat().format(count / time * 1000) + " t/s"); } private static CountService createJdkDynamicProxy(final CountService delegate) { CountService jdkProxy = (CountService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{CountService.class},new JdkHandler(delegate)); return jdkProxy; } private static class JdkHandler implements InvocationHandler { final Object delegate; JdkHandler(Object delegate) { this.delegate = delegate; } public Object invoke(Object object,Method method,Object[] objects) throws Throwable { return method.invoke(delegate,objects); } } private static CountService createCglibDynamicProxy(final CountService delegate) throws Exception { Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibInterceptor(delegate)); enhancer.setInterfaces(new Class[]{CountService.class}); CountService cglibProxy = (CountService) enhancer.create(); return cglibProxy; } private static class CglibInterceptor implements MethodInterceptor { final Object delegate; CglibInterceptor(Object delegate) { this.delegate = delegate; } public Object intercept(Object object,Object[] objects,MethodProxy methodProxy) throws Throwable { return methodProxy.invoke(delegate,objects); } } private static CountService createJavassistDynamicProxy(final CountService delegate) throws Exception { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(new Class[]{CountService.class}); Class<?> proxyClass = proxyFactory.createClass(); CountService javassistProxy = (CountService) proxyClass.newInstance(); ((ProxyObject) javassistProxy).setHandler(new JavaAssitInterceptor(delegate)); return javassistProxy; } private static class JavaAssitInterceptor implements MethodHandler { final Object delegate; JavaAssitInterceptor(Object delegate) { this.delegate = delegate; } public Object invoke(Object self,Method m,Method proceed,Object[] args) throws Throwable { return m.invoke(delegate,args); } } private static CountService createJavassistBytecodeDynamicProxy(CountService delegate) throws Exception { ClassPool mPool = new ClassPool(true); CtClass mCtc = mPool.makeClass(CountService.class.getName() + "JavaassistProxy"); mCtc.addInterface(mPool.get(CountService.class.getName())); mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc)); mCtc.addField(CtField.make("public " + CountService.class.getName() + " delegate;",mCtc)); mCtc.addMethod(CtNewMethod.make("public int count() { return delegate.count(); }",mCtc)); Class<?> pc = mCtc.toClass(); CountService bytecodeProxy = (CountService) pc.newInstance(); Field filed = bytecodeProxy.getClass().getField("delegate"); filed.set(bytecodeProxy,delegate); return bytecodeProxy; } private static CountService createAsmBytecodeDynamicProxy(CountService delegate) throws Exception { ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); String className = CountService.class.getName() + "AsmProxy"; String classPath = className.replace('.','/'); String interfacePath = CountService.class.getName().replace('.','/'); classWriter.visit(Opcodes.V1_5,Opcodes.ACC_PUBLIC,classPath,null,"java/lang/Object",new String[]{interfacePath}); MethodVisitor initVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC,"<init>","()V",null); initVisitor.visitCode(); initVisitor.visitVarInsn(Opcodes.ALOAD,0); initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,"()V"); initVisitor.visitInsn(Opcodes.RETURN); initVisitor.visitMaxs(0,0); initVisitor.visitEnd(); FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC,"delegate","L" + interfacePath + ";",null); fieldVisitor.visitEnd(); MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC,"count","()I",null); methodVisitor.visitCode(); methodVisitor.visitVarInsn(Opcodes.ALOAD,0); methodVisitor.visitFieldInsn(Opcodes.GETFIELD,"L" + interfacePath + ";"); methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE,interfacePath,"()I"); methodVisitor.visitInsn(Opcodes.IRETURN); methodVisitor.visitMaxs(0,0); methodVisitor.visitEnd(); classWriter.visitEnd(); byte[] code = classWriter.toByteArray(); CountService bytecodeProxy = (CountService) new ByteArrayClassLoader().getClass(className,code).newInstance(); Field filed = bytecodeProxy.getClass().getField("delegate"); filed.set(bytecodeProxy,delegate); return bytecodeProxy; } private static class ByteArrayClassLoader extends ClassLoader { public ByteArrayClassLoader() { super(ByteArrayClassLoader.class.getClassLoader()); } public synchronized Class<?> getClass(String name,byte[] code) { if (name == null) { throw new IllegalArgumentException(""); } return defineClass(name,code,code.length); } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |