`
Kingson_Wu
  • 浏览: 111341 次
文章分类
社区版块
存档分类
最新评论

深入剖析动态代理--性能比较

 
阅读更多

原文:http://blog.csdn.net/liutengteng130/article/details/46565309


动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.

生成动态代理类的方法很多,如JDK自带的动态代理、CGLIBJavassist或者ASM库。

JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIBJavassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生辰工具。但ASM的使用是在过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。


JDK实现

1、步骤

1)通过实现InvocationHandler接口创建自己的调用处理器

2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类

3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型

4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入


2、创建代理

  1. //InvocationHandlerImpl实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
  2. //其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
  3. InvocationHandlerhandler=newInvocaitonHandlerImpl(..);
  4. //通过Proxy为包括Interface接口在内的一组接口动态创建代理类的对象
  5. Classclazz=Proxy.getProxyClass(classLoader,newClass[]{Interface.class,...});
  6. //通过反射从生成的类对象获得构造函数对象
  7. Constructorconstructor=clazz.getConstructor(newClass[]{InvocationHandler.class});
  8. //通过构造函数对象创建动态代理类实例
  9. InterfaceProxy=(Interface)constructor.newInstance(newObject[]{handler});
  10. //Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。
  11. //InvocationHandlerImpl实现了InvocaitonHandler接口,并能实现方法调用从代理类到委托类的分派转发
  12. InvocaitonHandlerhandler=newInvocationHandlerImpl(..);
  13. //通过Proxy直接创建动态代理类实例
  14. nterfaceproxy=(Interface)Proxy.newProxyInstance(classLoader,newClass[]{Interface.class},handler);

3、代码

  1. /**
  2. *接口
  3. *@authorEmily
  4. *
  5. */
  6. publicinterfaceIDBQuery{
  7. Stringrequest();
  8. }
  9. /**
  10. *真实的实现类,具体的目标对象
  11. *@authorEmily
  12. *
  13. */
  14. publicclassDBQueryimplementsIDBQuery{
  15. publicDBQuery(){
  16. try{
  17. Thread.sleep(1000);//可能包含数据库连接等耗时操作
  18. }catch(InterruptedExceptione){
  19. e.printStackTrace();
  20. }
  21. }
  22. @Override
  23. publicStringrequest(){
  24. return"requeststring";
  25. }
  26. }
  27. /**
  28. *JDK动态代理的实现类
  29. *@authorEmily
  30. *
  31. */
  32. publicclassJdkDbQueryHandlerimplementsInvocationHandler{
  33. IDBQueryreal=null;//主题接口
  34. /**
  35. *生成Handler
  36. */
  37. @Override
  38. publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
  39. throwsThrowable{
  40. if(real==null)
  41. real=newDBQuery();//如果是第一次调用,则生成真实对象
  42. returnreal.request();//使用真实主题完成实际的操作
  43. }
  44. /**
  45. *利用Handler生成动态代理对象
  46. *@return
  47. */
  48. publicstaticIDBQuerycreateJdkProxy(){
  49. //根据指定的类加载器和接口以及截获器,返回代理类的一个实例对象
  50. //ClassLoaderloader:指定被代理对象的类加载器
  51. //Class[]Interfaces:指定被代理对象所以事项的接口
  52. //InvocationHandlerh:指定需要调用的InvocationHandler对象
  53. IDBQueryjdkProxy=(IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),newClass[]{IDBQuery.class},newJdkDbQueryHandler());
  54. returnjdkProxy;
  55. }
  56. }

执行过程:


如图,目标对象的方法调用被Proxy拦截,在InvocationHandler中的回调方法中通过反射调用,这种动态代理的方法实现了对类的方法的运行时修改。

JDK动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。准确的说这不应该是缺点,一个良好的系统,每个类都应该有一个接口与之对应。针对接口编程。

CGLIB实现

  1. packagecom.ltt.dynamic;
  2. importjava.lang.reflect.Method;
  3. importnet.sf.cglib.proxy.Enhancer;
  4. importnet.sf.cglib.proxy.MethodInterceptor;
  5. importnet.sf.cglib.proxy.MethodProxy;
  6. /**
  7. *CGLIB动态代理
  8. *@authorEmily
  9. *
  10. */
  11. publicclassCglibDbQueryInterceptorimplementsMethodInterceptor{
  12. IDBQueryreal=null;
  13. /**
  14. *处理代理逻辑的切入类
  15. */
  16. @Override
  17. publicObjectintercept(Objectarg0,Methodarg1,Object[]arg2,
  18. MethodProxyarg3)throwsThrowable{
  19. if(real==null){//代理类的内部逻辑
  20. real=newDBQuery();
  21. returnreal.request();
  22. }
  23. returnnull;
  24. }
  25. /**
  26. *生成动态代理
  27. *@return
  28. */
  29. publicstaticIDBQuerycreateCglibProxy(){
  30. Enhancerenhancer=newEnhancer();
  31. //指定切入器,定义代理类逻辑
  32. enhancer.setCallback(newCglibDbQueryInterceptor());
  33. //指定实现的接口
  34. enhancer.setInterfaces(newClass[]{IDBQuery.class});
  35. IDBQuerycglibProxy=(IDBQuery)enhancer.create();
  36. returncglibProxy;
  37. }
  38. }


Javassist实现

一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler:例如createJavassistDynProxy();使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑,如createJavassistBytecodeDynamicProxy()方法。

  1. /**
  2. *Javassist动态代理
  3. *@authorEmily
  4. *
  5. */
  6. publicclassJavassistDynDbQueryHandlerimplementsMethodHandler{
  7. IDBQueryreal=null;
  8. /*
  9. *(non-Javadoc)实现用于代理逻辑处理的Handler
  10. *@seejavassist.util.proxy.MethodHandler#invoke(java.lang.Object,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Object[])
  11. */
  12. @Override
  13. publicObjectinvoke(Objectarg0,Methodarg1,Methodarg2,Object[]arg3)
  14. throwsThrowable{
  15. if(real==null)
  16. real=newDBQuery();
  17. returnreal.request();
  18. }
  19. /**
  20. *创建动态代理
  21. *@return
  22. *@throwsException
  23. */
  24. publicstaticIDBQuerycreateJavassistDynProxy()throwsException{
  25. ProxyFactoryproxyFactory=newProxyFactory();
  26. proxyFactory.setInterfaces(newClass[]{IDBQuery.class});//指定接口
  27. ClassproxyClass=proxyFactory.createClass();
  28. IDBQueryjavassistProxy=(IDBQuery)proxyClass.newInstance();//设置Handler处理器
  29. ((ProxyObject)javassistProxy).setHandler(newJavassistDynDbQueryHandler());
  30. returnjavassistProxy;
  31. }
  32. /**
  33. *运行时生成业务逻辑
  34. *@return
  35. *@throwsException
  36. */
  37. publicstaticIDBQuerycreateJavassistBytecodeDynamicProxy()throwsException{
  38. ClassPoolmPool=newClassPool(true);
  39. //定义类名
  40. CtClassmCtc=mPool.makeClass(IDBQuery.class.getName()+"JavaassistBytecodeProxy");
  41. //需要实现接口
  42. mCtc.addInterface(mPool.get(IDBQuery.class.getName()));
  43. //添加构造函数
  44. mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
  45. //添加类的字段信息,使用动态Java代码
  46. mCtc.addField(CtField.make("public"+IDBQuery.class.getName()+"real;",mCtc));
  47. StringdbQueryname=DBQuery.class.getName();
  48. //添加方法,这里使用动态Java代码指定内部逻辑
  49. mCtc.addMethod(CtNewMethod.make("publicStringrequest(){if(real==null)real=new"+dbQueryname+"();returnreal.request();}",mCtc));
  50. //基于以上信息,生成动态类
  51. Classpc=mCtc.toClass();
  52. //生成动态类的实例
  53. IDBQuerybytecodeProxy=(IDBQuery)pc.newInstance();
  54. returnbytecodeProxy;
  55. }
  56. }

三种动态代理:JDK动态代理创建速度优于CGLIB动态代理,但是在函数的调用性能上远不如CGLIBJavassist。故CGLIB和Javassist整体性能上比JDK动态代理好。

性能比较:

  1. CreateJDKProxy:13ms
  2. CreateCGLIBProxy:217ms
  3. CreateJAVAASSISTProxy:99ms
  4. CreateJAVAASSISTBytecodeProxy:168ms
  5. ================
  6. RunJDKProxy:2224ms,634,022t/s
  7. RunCGLIBProxy:1123ms,1,255,623t/s
  8. RunJAVAASSISTProxy:3212ms,438,999t/s
  9. RunJAVAASSISTBytecodeProxy:206ms,6,844,977t/s
  10. ----------------
  11. RunJDKProxy:2169ms,650,099t/s
  12. RunCGLIBProxy:1059ms,1,331,506t/s
  13. RunJAVAASSISTProxy:3328ms,423,697t/s
  14. RunJAVAASSISTBytecodeProxy:202ms,6,980,521t/s
  15. ----------------
  16. RunJDKProxy:2174ms,648,604t/s
  17. RunCGLIBProxy:1032ms,1,366,342t/s
  18. RunJAVAASSISTProxy:3119ms,452,088t/s
  19. RunJAVAASSISTBytecodeProxy:207ms,6,811,910t/s
  20. ----------------


Javassist字节码最快,CGLIB次之,是JDK的两倍。

它们都是通过字节码生成来实现动态代理的。只不过是它们生成的字节码不一样,像JDK,CGLIB都考虑了很多因素,以及继承或包装了自己的一些类,所以生成的字节码非常大,而我们很多时候用不上这些,手工生成的字节码非常小(Javassist是手工生成的字节码),所以速度快。

另外,ASM也是手工生成的字节码,速度也很快,但是它没有一个数量级,通常情况下选用Javassist生成字节码的方式。

分享到:
评论

相关推荐

    Linux高性能服务器编程

    第二部分对高性能服务器编程的核心要素进行了全面深入的剖析,包含Linux网络编程API、高级I/O函数、Linux服务器程序规范、高性能服务器程序框架、I/O复用、信号、定时器、高性能I/O框架库Libevent、多进程编程、多...

    Java思维导图xmind文件+导出图片

    Nginx反向代理服务器及负载均衡服务器配置实战 利用keepalived+Nginx实战Nginx高可用方案 基于Nginx实现访问控制、连接限制 Nginx动静分离实战 Nginx Location ReWrite 等语法配置及原理分析 Nginx提供https...

    深入理解Nginx:模块开发与架构解析.陶辉(带详细书签)

    同时书中还深入剖析了Nginx架构,帮助你深入了解Ngnix原理,以便在面对Nginx问题时,你也可以找到最合适的方法修复或者规避问题。另外,本书还展示了Nginx在服务器开发上的许多巧妙设计,了解这些技巧可以帮助你拓展...

    asp.net知识库

    深入剖析ASP.NET组件设计]一书第三章关于ASP.NET运行原理讲述的补白 asp.net 运行机制初探(httpModule加载) 利用反射来查看对象中的私有变量 关于反射中创建类型实例的两种方法 ASP.Net应用程序的多进程模型 NET委托...

    SQL Server 2008管理员必备指南(超高清PDF)Part2

    SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 作者简介 作者:(美国)斯坦里克 (William R.Stanek) 译者:贾洪峰 William R.Stanek...

    SQL.Server.2008管理员必备指南.part1.rar(1/4)

     SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 编辑本段 目录  第Ⅰ部分 SQL Server 2008管理基础  第1章 SQL Server 2008管理...

    深入理解Nginx

    本书是阿里巴巴资深Nginx技术专家呕心沥血之作,是作者多年的经验结晶,也是目前市场上唯一一本通过还原Nginx设计思想,剖析Nginx架构来帮助读者快速高效开发HTTP模块的图书。本书首先通过介绍官方Nginx的基本用法和...

    SQL Server 2008管理员必备指南(超高清PDF)Part1

    SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 作者简介 作者:(美国)斯坦里克 (William R.Stanek) 译者:贾洪峰 William R.Stanek...

    SQL Server 2008管理员必备指南(超高清PDF)Part3

    SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 作者简介 作者:(美国)斯坦里克 (William R.Stanek) 译者:贾洪峰 William R.Stanek...

    SQL.Server.2008管理员必备指南.part2.rar(2/4)

     SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 编辑本段 目录  第Ⅰ部分 SQL Server 2008管理基础  第1章 SQL Server 2008管理...

    SQL.Server.2008管理员必备指南.part3.rar(3/4)

     SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 编辑本段 目录  第Ⅰ部分 SQL Server 2008管理基础  第1章 SQL Server 2008管理...

    SQL.Server.2008管理员必备指南.part4.rar(4/4)

     SQL Server专家的呕心力作,数据库管理员的实战宝典,全面、深入地剖析SQL Server2008新特性,结构独特,实例丰富,操作性强。 编辑本段 目录  第Ⅰ部分 SQL Server 2008管理基础  第1章 SQL Server 2008管理...

    二十三种设计模式【PDF版】

    以 Jive 为例,剖析代理模式在用户级别授权机制上的应用 设计模式之 Facade(门面?) 可扩展的使用 JDBC针对不同的数据库编程,Facade提供了一种灵活的实现. 设计模式之 Composite(组合) 就是将类用树形结构组合成...

    精通AngularJS part1

    51AngularJS表单与传统表单的比较139 介绍ngModel指令141 52创建用户信息表单142 53理解输入指令143 添加所需验证143 使用基于文本的输入(text、textarea、e—mail、URL、number)143 使用checkbox输入144 ...

    庖丁解牛纵向切入ASP.NET 3.5控件和组件开发技术.pdf

     本书主要介绍asp.net的控件开发,书中通过70多个例子讲解了asp.net控件开发技术的各个方面,而且剖析了很多控件中系统基类源代码,读者从这些系统源代码可以体会设计模式思想。如果扎实地掌握了asp.net控件的运行...

    网上会展的未来发展趋势

    网上会展的实施依赖于现代计算机信息技术,由于计算机网络信息的全球性、开放性、扩散性、共享性和动态性等特性,它在存储、处理、使用和传输上存在严重脆弱性,易于受计算机病毒的感染,数据被干扰、遗漏、丢失,...

Global site tag (gtag.js) - Google Analytics