最近需要通过配置生成代码,减少重复编码和维护成本。用到了一些动态的特性,和大家分享下心得。

我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是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

from:https://www.cnblogs.com/hucn/p/3636912.html

Java动态编程初探——Javassist相关推荐

  1. 秒懂Java动态编程(Javassist简单入门级)

    概述 什么是动态编程?动态编程解决什么问题?Java中如何使用?什么原理?如何改进? 什么是动态编程 动态编程是相对于静态编程而言的,平时我们讨论比较多的就是静态编程语言,例如Java,与动态编程语言 ...

  2. Java动态编程初探

    作者简介 传恒,一个喜欢摄影和旅游的软件工程师,先后从事饿了么物流蜂鸟自配送和蜂鸟众包的开发,现在转战 Java,目前负责物流策略组分流相关业务的开发. 什么是动态编程 动态编程是相对于静态编程而言的 ...

  3. Java动态编程之javassist

    概述 Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果.熟练使用这套工具,可以让Java编程更接近与动态语言编程. 教程 ma ...

  4. Java动态编程技术

    Java是一种静态显式强类型语言,导致java代码存在大量的模版化代码,一直被人诟病开发效率低.其实java语言本身以及JVM生态提供了大量动态编程技术,可以大量减少模版化代码.本文尝试整理相关的技术 ...

  5. java动态编程解决分硬币问题,动态编程硬币更改问题

    我在理解各种问题的动态编程解决方案时遇到问题,特别是硬币找零问题: "给定值N,如果我们要N分钱找零,并且我们有无限数量的S = {S1,S2,..,Sm}硬币的供应,我们可以用几种方法进行 ...

  6. JVM插桩之四:Java动态代理机制的对比(JDK和CGLIB,Javassist,ASM)

    一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...

  7. 菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

    菜鸟学习笔记:Java提升篇12(Java动态性2--动态编译.javassist字节码操作) Java的动态编译 通过脚本引擎执行代码 Java字节码操作 JAVAssist的简单使用 常用API ...

  8. Java是动态语言吗?从《Java核心编程》探索真知

    目录 一.Java是动态语言吗? 1.动态语言 2.静态类型 3.<Java核心编程>中探索~~为什么Java可以称之为"准动态语言"? 二.了解ClassLoader ...

  9. 【Java高级】初探socket编程 ——JavaSocket连接与简单通信

    新学期生活开始一段时间了,要继续学习一些新的技术(这里指socket /doge),目标是尝试完成一个在线即时聊天的小程序(尽量不咕).会更新一系列socket编程的技术文章,欢迎关注交流~ 那么千里 ...

最新文章

  1. RxJava 过滤操作符 throttleFirst 与 throttleLast 以及 sample
  2. 2016各大公司校招薪水曝光:年薪28万,这只是零花钱
  3. linux学习(3) 关机使命
  4. u-boot命令寻找分析--find_cmd函数
  5. 病毒加壳技术与脱壳杀毒方法解析
  6. .NET Core实战项目之CMS 第九章 设计篇-白话架构设计
  7. 剑指offer——二叉搜索树的后序遍历序列
  8. window.createPopup()用法以及短消息提示框代码
  9. 开源GIS(三)——openlayers地图基本框架与天地图加载
  10. Linux_ubuntu16.04 无线/Wifi 上网速度慢的解决方法
  11. 5G时代下的移动边缘计算(MEC)探索系列之二
  12. 数据挖掘:原理与实践(基础篇)(进阶篇)
  13. 3Dmax玻璃材质参数应该怎样设置
  14. 百度热力图和高德热力图对比
  15. 中山大学2021计算机考研复试线,2021中山大学研究生复试分数线
  16. no input file specified解决方法
  17. 阿里云配置负载均衡以及遇到的问题
  18. 使mac支持NTFS读写问题
  19. Win11键盘打不出字怎么办 windows11键盘打不出字的解决方法
  20. [Bootstrap案例练习]——现代浏览器博物馆

热门文章

  1. What is corresponding Cron expression to fire in every X seconds, where X 60? --转载
  2. Jackson学习二之集合类对象与JSON互相转化--转载
  3. no awt in java.libary.path
  4. XGBoost的基本使用应用Kaggle便利店销量预测
  5. 【风控体系】携程基于大数据分析的实时风控体系
  6. sklearn-GridSearchCV调节超参数
  7. 基于Keras搭建cifar10数据集训练预测Pipeline
  8. Exercise: Logistic Regression and Newton's Method
  9. 深入理解分布式技术 - Kafka 高性能原理剖析
  10. 白话Elasticsearch16-深度探秘搜索技术之使用原生cross-fiedls技术解决搜索弊端