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

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

 
阅读更多

原文: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生成字节码的方式。

分享到:
评论

相关推荐

    关于jdk动态代理的源码剖析

    ### 关于JDK动态代理的源码剖析 #### 一、引言 在Java开发过程中,动态代理技术是一项非常实用的技术,它可以帮助我们实现在不修改原有代码的基础上为方法增加额外的功能,比如日志记录、权限校验等。本文将深入...

    深入剖析tomcat (完整目录)

    【深入剖析Tomcat——完整目录】 Tomcat是Apache软件基金会的Jakarta项目中的一个核心项目,是一个开源的、免费的Web应用服务器,主要用于运行Java Servlet和JavaServer Pages(JSP)。Tomcat以其轻量级、易用性...

    深入剖析Tomcat .rar

    【深入剖析Tomcat】 Tomcat是一款广泛使用的开源Java Servlet容器,由Apache软件基金会开发和维护。它是基于Java的Web应用程序的服务器,特别是用于部署和运行Java Servlets和JSP(JavaServer Pages)技术。本篇...

    《深入剖析Tomcat》的光盘源码

    通过学习《深入剖析Tomcat》的源码,开发者不仅可以提升对Tomcat工作原理的理解,还能提升解决问题的能力,为解决实际开发中的性能瓶颈和安全问题提供强有力的支持。同时,对Tomcat源码的深入理解也有助于向其他Java...

    深入剖析TOMCAT+Tomcat权威指南(第二版)

    《深入剖析TOMCAT+Tomcat权威指南(第二版)》是两本关于Apache Tomcat服务器的重量级著作,它们详尽地阐述了Tomcat的内部工作机制、配置、优化以及故障排查等方面的知识,旨在帮助读者从新手到专家,全面掌握这款广泛...

    深入剖析Spring Web源码(含一二版)带目录

    《深入剖析Spring Web源码(含一二版)》是一本深度解析Spring Web框架核心机制的书籍,涵盖了Spring MVC和Spring Web的重要源码分析。这本书旨在帮助开发者深入理解Spring Web的工作原理,提升对Spring框架的使用和...

    Hibernate 延迟加载剖析与代理模式应用

    ### Hibernate延迟加载剖析与代理模式应用 #### 一、引言 在现代软件开发中,尤其是在基于Java的企业级应用开发中,Hibernate作为一种流行的ORM(Object Relational Mapping)框架,极大地简化了开发者对数据库的...

    hibernate 延迟加载深入剖析

    ### Hibernate延迟加载深入剖析 #### 一、概述 在现代软件开发中,特别是与数据库交互的应用场景下,Hibernate作为一款流行的Java持久层框架,提供了多种高效处理数据的技术。其中,延迟加载(Lazy Loading)是一...

    深入剖析NGINGX

    【深入剖析NGINX】是关于这款高性能、高并发的Web服务器和反向代理服务器的详细解析。NGINX以其高效的并发处理能力、低内存占用和丰富的模块化设计,在全球范围内广泛应用于各种规模的网站和应用程序。这本书可能是...

    深入剖析Tomcat

    **深入剖析Tomcat** Tomcat,作为Apache软件基金会的项目之一,是一款开源的Java Servlet容器,主要用于实现Java Web应用程序的部署和运行。它基于Java Servlet和JavaServer Pages(JSP)技术,是许多开发者和企业...

    深入剖析Spring Web源码 pdf高清版(第二版)

    《深入剖析Spring Web源码》(第二版) 是一本针对Java开发者深度解析Spring Web框架核心原理的权威指南。这本书详细解读了Spring MVC和Spring WebFlux两大核心模块的源码,帮助读者理解Spring如何实现高效的Web应用...

    深入剖析MyBatis SQL执行流程:从配置到查询结果的全程追踪

    本文将深入探讨MyBatis的SQL执行流程,从配置解析到查询结果的返回,以及Spring如何整合MyBatis,特别是如何管理Mapper接口的动态代理。 1. **配置解析与SqlSessionFactory的创建** MyBatis通过...

    java反射深入剖析(推荐)

    本文将深入剖析Java反射的概念及其在实际开发中的应用。 首先,我们需要了解,反射的核心在于`java.lang.Class`类。每个类在Java中都有一个与之对应的`Class`对象,它包含了类的所有元数据信息。例如,在案例1中,...

    java源码剖析-interesting-java:Java基础源码剖析

    在"interesting-java"这个项目中,我们主要关注的是对Java基础源码的深入剖析。Java是一种广泛使用的面向对象的编程语言,它的设计目标是具有良好的可移植性、高性能和简单的语法。通过研究源码,我们可以更好地理解...

    java学习资料-反射机制深入理解剖析

    5. 创建动态代理,通过`java.lang.reflect.Proxy`类,可以在运行时生成代理对象来实现特定接口。 在实际开发中,反射机制常用于: - 框架设计:Spring框架中的依赖注入、AOP切面编程等都大量使用了反射。 - 动态...

    深入剖析Visual Studio 2010软件测试新功能(DEV301).rar

    《深入剖析Visual Studio 2010软件测试新功能》 Visual Studio 2010作为微软开发工具的重要版本,引入了一系列创新的软件测试功能,极大地提升了开发人员和测试工程师的工作效率,为软件质量保障提供了更为强大的...

    java源码剖析-javase-base-resource:从源码角度剖析不一样的JavaSE,对应博文请访问

    7. **反射与动态代理**:反射机制允许我们在运行时检查和操作类、接口和对象,而动态代理则可以实现AOP(面向切面编程)。这两个特性在框架开发和插件化设计中广泛使用。 8. **泛型**:泛型提高了代码的类型安全性...

    RocketMQ高级原理:深入剖析消息系统的核心机制

    《RocketMQ高级原理:深入剖析消息系统的核心机制》是一篇深度解析RocketMQ核心机制的文章,旨在帮助读者理解和掌握这款分布式消息中间件的工作原理。RocketMQ是阿里巴巴开源的一款高性能、高可用的消息中间件,广泛...

    深入Java虚拟机附源码

    10. **反射与动态代理**:Java反射机制允许我们在运行时检查和修改类的行为,动态代理则可以在运行时创建代理对象,两者在插件化开发、AOP(面向切面编程)等领域有广泛应用。 通过对《深入Java虚拟机》的阅读和...

Global site tag (gtag.js) - Google Analytics