最近需要通过配置生成代码,减少重复编码和维护成本。用到了一些动态的特性,和大家分享下心得。
我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit。
Javassit其实就是一个二方包,提供了运行时操作Java字节码的方法。大家都知道,Java代码编译完会生成.class文件,就是一堆字节码。JVM(准确说是JIT)会解释执行这些字节码(转换为机器码并执行),由于字节码的解释执行是在运行时进行的,那我们能否手工编写字节码,再由JVM执行呢?答案是肯定的,而Javassist就提供了一些方便的方法,让我们通过这些方法生成字节码。
类似字节码操作方法还有ASM。几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象。在实现成本上Javassist和反射都很低,而ASM由于直接操作字节码,相比Javassist源码级别的api实现成本高很多。几个方法有自己的应用场景,比如Kryo使用的是ASM,追求性能的最大化。而NBeanCopyUtil采用的是Javassist,在对象拷贝的性能上也已经明显高于其他的库,并保持高易用性。实际项目中推荐先用Javassist实现原型,若在性能测试中发现Javassist成为了性能瓶颈,再考虑使用其他字节码操作方法做优化。
Javassist的使用很简单,首先获取到class定义的容器ClassPool,通过它获取已经编译好的类(Compile time class),并给这个类设置一个父类,而writeFile讲这个类的定义从新写到磁盘,以便后面使用。
ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("test.Rectangle"); cc.setSuperclass(pool.get("test.Point")); cc.writeFile();
由CtClass可以方便的获取字节码和加载字节码:
byte[] b = cc.toBytecode(); Class clazz = cc.toClass();
如果需要定义一个新类,只需要
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
同样的还可以通过CtMethod和CtField构造方法和成员甚至Annotation。
ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("foo"); CtMethod mthd = CtNewMethod.make("public Integer getInteger() { return null; }", cc); cc.addMethod(mthd);
CtField f = new CtField(CtClass.intType, "i", cc); point.addField(f);
clazz = cc.toClass(); Object instance = class.newInstance();
Javassist不仅可以生成类、变量和方法,还可以操作现有的方法,这在AOP上非常有用,比如做方法调用的埋点
// Point.java class Point { int x, y; void move(int dx, int dy) { x += dx; y += dy; } } // 对已有代码每次move执行时做埋点 ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.get("Point"); CtMethod m = cc.getDeclaredMethod("move"); m.insertBefore("{ System.out.println($1); System.out.println($2); }"); cc.writeFile();
其中$1和$2表示调用栈中的第一和第二个参数,写到磁盘后的class定义类似:
class Point { int x, y; void move(int dx, int dy) { { System.out.println(dx); System.out.println(dy); } x += dx; y += dy; } }
在使用Javassist时遇到过一些问题。
1 因为tomcat和jboss使用的是独立的classloader,而Javassist是通过默认的classloader加载类,因此直接对tomcat context中定义的类做toClass会抛出ClassCastException异常,可以用tomcat的classloader加载字节码。
CtClass cc = ...;
Class c = cc.toClass(bean.getClass().getClassLoader());
2 发现在简单的测试中可以load的类,在tomcat中无法load。这是因为,ClassPool.getDefault()查找的路径和底层的JVM路径。而tomcat中定义了多个classloader,因此额外的class路径需要注册到ClassPool中。
pool.insertClassPath(new ClassClassPath(this.getClass()));
3 我想在运行时修改类的一个方法,但是JVM是不允许动态的reload类定义的。一旦classloader加载了一个class,在运行时就不能重新加载这个class的另一个版本,调用toClass()会抛LinkageError。因此需要绕过这种方式定义全新的class。而toClass()其实是当前thread所在的classloader加载class。
4 Javassist生成的字节码由于没有class声明,字节码创建变量及方法调用都需要通过反射。这点在在线的应用上的性能损失是不能接受的,受到NBeanCopyUtil实现的启发,可以定义一个Interface,Javassist的字节码实现这个Interface,而调用方通过这个接口调用字节码,而不是反射,这样避免了反射调用的开销。还有一点字节码new一个变量也是通过反射,因此通过代理的方法,将每个pv都需要new的字节码对象改为每次new一个代理对象,代理到常驻内存的字节码对象中,这样避免了每次反射的开销。
参考资料:
http://asm.ow2.org/
http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html
http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/tutorial/tutorial.html
http://www.ibm.com/developerworks/cn/java/coretech/java-dynamic.html
相关推荐
在计算机软件开发中,Java编程语言的应用是极为广泛与深远的。Java语言自问世以来就以其独特的特性和优势吸引了大量开发者的注意,这些特性包括但不限于其平台独立性、内存管理优化、面向对象的编程设计以及其强大的...
这份资料"基于计算机软件开发的JAVA编程应用初探"将引领我们深入理解Java在软件开发中的核心概念、应用领域以及实战技巧。 首先,Java的基础知识是学习的重点。Java是一种强类型、静态类型的编程语言,它的语法与...
Java高级编程课程思政案例教学初探.pdf
"计算机软件开发中Java编程语言的应用初探" 计算机软件开发中Java编程语言的应用初探是计算机软件开发的重要组成部分,Java编程语言是一种广泛使用的编程语言,具有许多优点和特征,使其在软件开发中应用非常广泛。...
除了上述领域,Java编程语言还被应用于计算机图像处理技术,如板形识别技术中。研究人员通过增强计算机系统捕捉物体运动图像的效率,利用高分辨率相机来提高图像质量和信息的准确率,进而在医学、工业和通信等领域...
Java高级编程课程思政案例教学初探,是一个深入探讨如何将思想政治教育融入到Java高级编程教学中的主题。在当前的教育环境中,强调立德树人,将思政元素与专业课程相结合,旨在培养具备良好品德和社会责任感的IT人才...
本文所探讨的“基于Java的开源GIS编程教学初探”正是为了满足这一教学需求,通过实践教学的方式,培养学生利用Java语言进行开源GIS编程的综合能力。 Java语言作为一门成熟稳定的编程语言,在开源软件开发领域拥有...
Java教学方法初探 ...Java教学方法初探强调了学习兴趣的培养、实例化教学、实例化先果后因式教学和重编程思想而轻语法等几个重要的教学方法,旨在提高学生的学习兴趣和应用知识能力,达到教学效果的最优化。
Java NIO 网络编程初探 1. Java NIO Java 1.4 版本添加了一个新的IO API,称为NIO(New IO)。NIO拥有所有IO的功能,但是操作方法却完全不一样。NIO支持面向缓冲区的、基于通道的IO操作。能够更加高效的进行IO操作。...
总的来说,"基于游戏开发的Java语言教学初探"这个主题涵盖了从基础语法到高级应用的多个层面,旨在通过实践性的游戏项目,使学生在愉快的氛围中掌握Java编程技能,并了解游戏开发的全貌。通过阅读提供的"基于游戏...
基于游戏开发的Java语言教学初探 本文讨论了基于游戏开发的Java语言教学初探,旨在探讨如何培养学生的理论能力、分析能力、开发能力和实践能力。文章从教学设想和开发工具两个方面入手,介绍了Java语言的游戏构架、...
高职Java课程设计初探 本文探讨高职院校Java课程设计的重要性和实施策略。课程建设与改革是高职院校提高教学质量的核心,也是教学改革的重点和难点。长期以来,传统学科性课程始终统治着我国的职业教育,但职业教育...
通过阅读《Netty初探:掌握高性能网络通信框架,提升Java网络编程技能》这本书,你将深入了解这些概念,并学会如何运用Netty构建实际的网络应用,提升你的Java编程技能。这本书会逐步引导你从基础知识到高级特性的...
《java完美编程(第3版)》侧重于面向对象设计,通过浅显易懂的语言和代码,很好地平衡了完整实例和解释性讨论,并围绕着java语言的特征全面、透彻地介绍诸多编程技术。书中全面介绍java语言已经实现的封装、继承和...
教师可以通过设计小型游戏项目,让学生逐步掌握Java编程的基本语法、数据结构、控制结构等核心概念。同时,游戏开发中的问题解决过程也能锻炼学生的逻辑思维和团队协作能力。 5. **教学效果** 结合游戏开发的教学...
在Java编程环境中,有许多开源GIS库可供开发者使用,这使得Java成为GIS开发的一个强大平台。本教程将探讨如何利用Java进行开源GIS编程,以实现地理数据的处理和应用开发。 一、Java GIS框架 1. GeoTools:GeoTools...
常用的Java开发工具有JCreatorPro、JDK、Eclipse以及SQLServer2008等,这些都是进行Java编程和开发所必需的工具。 游戏开发作为一种教学手段,具有内容简单、容易理解和操作上手快等优势。它能够吸引广大青少年群体...
"软件工程专业Java教学初探" 本文探讨了软件工程专业Java教学的初步研究,旨在解决高校Java课程无法满足企业实际需求的问题。文章首先指出了中国软件人才的需求严重不足,特别是Java人才的缺口非常大。然后,文章...