zoukankan      html  css  js  c++  java
  • JDK、CGLIB、Javassist和ASM的动态代理使用对比

    动态代理是指在运行时,动态生成代理类。正如标题所示,能够提供动态代理的方式有很多。既然选择这么有很多,那么如何选择会更好呢?

    带着这个疑问,我找到了Dubbo的作者——梁飞大神的一篇文章。文章中对四种方式都做了性能测试,从当时测试的结果来看,Javassist成了最好的选择。

    不过时间过了那么久,现在在JDK 1.8上,根据上面的测试用例测试,会发现JDK动态代理和CGLIB动态代理的性能提升了很多。

    测试的版本信息如下:
    1.8.0_201,cglib-3.2.5,javassist-3.12.1.GA,asm使用的是JDK自带的。

    测试结果如下:

    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 JAVAASSIST Bytecode Proxy: 183 ms, 273,224,000 t/s
    Run ASM Bytecode Proxy: 188 ms, 265,957,000 t/s
    ----------------
    Run JDK Proxy: 288 ms, 173,611,000 t/s
    Run CGLIB Proxy: 272 ms, 183,823,000 t/s
    Run JAVAASSIST Proxy: 1469 ms, 34,036,000 t/s
    Run JAVAASSIST Bytecode Proxy: 188 ms, 265,957,000 t/s
    Run ASM Bytecode Proxy: 179 ms, 279,329,000 t/s
    ----------------
    

    从测试结果来看,JDK代理和CGLIB代理性能已经有很大的提升,ASM与Javassist在性能上已经拉不开差距了。

    在易用性方面:

    1. JDK代理是最简单方便的,只需要使用Proxy和InvocationHandler两个类,不过只能代理接口。
    2. 其次是CGLIB,也很方便,不过需要引入CGLIB的JAR包。
    3. Javassist需要用用字符串拼接Java源代码,稍微会比较繁琐。
    4. 最麻烦的是ASM,需要手工写字节码,一般人可能还写不出来。

    在代理对象的创建速度上,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, Method method, 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, null);
            initVisitor.visitCode();
            initVisitor.visitVarInsn(Opcodes.ALOAD, 0);
            initVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            initVisitor.visitInsn(Opcodes.RETURN);
            initVisitor.visitMaxs(0, 0);
            initVisitor.visitEnd();
    
            FieldVisitor fieldVisitor = classWriter.visitField(Opcodes.ACC_PUBLIC, "delegate", "L" + interfacePath + ";", null, null);
            fieldVisitor.visitEnd();
    
            MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "count", "()I", null, null);
            methodVisitor.visitCode();
            methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
            methodVisitor.visitFieldInsn(Opcodes.GETFIELD, classPath, "delegate", "L" + interfacePath + ";");
            methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, interfacePath, "count", "()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, 0, code.length);
            }
    
        }
    }
    
    
  • 相关阅读:
    javaday19_List接口_Set接口
    01玩转数据结构_04_最基础的动态数据结构:链表
    10 拖拽的对话框_滚动条_放大镜_
    01玩转数据结构_03_栈和队列
    java小技巧
    01玩转数据结构_02_不要小瞧数组
    01玩转数据结构_01_课程介绍
    javaday18_ArrayList
    JZOJ.3777【NOI2015模拟8.17】最短路(shortest)
    JZOJ.5230【NOIP2017模拟8.5】队伍统计
  • 原文地址:https://www.cnblogs.com/bluemilk/p/11397367.html
Copyright © 2011-2022 走看看