动态字节码技术 javassist 初探

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态字节码技术 javassist 初探相关的知识,希望对你有一定的参考价值。

字节码应用场景
AOP 技术、Lombok 去除重复代码插件、动态修改 class 文件等
字节码技术优势
Java 字节码增强指的是在 Java 字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改,Java 字节码增强主要是为了减少冗余代码,提高性能等 实现字节码增强的主要步骤为:
修改字节码,在内存中获取到原来的字节码,然后通过一些工具(如 ASM,Javaasist)来修改它的byte[]数组,得到一个新的byte数组
使修改后的字节码生效

自定义 ClassLoader 来加载修改后的字节码
替换掉原来的字节码,在 JVM 加载用户的 Class 时,拦截,返回修改后的字节码,或者在运行时,使用Instrumentation.redefineClasses 方法来替换掉原来的字节码
常见的字节码操作类库
BCEL
Byte Code Engineering Library(BCEL),这是Apache Software Foundation的 Jakarta 项目的一部分,BCEL 是 Java classworking 广泛使用的一种框架,它可以让您深入jvm汇编语言进行类库操作的细节,BCEL 与 javassist 有不同的处理字节码方法,BCEL 在实际的 JVM 指令层次上进行操作(BCEL 拥有丰富的 JVM 指令集支持)而 javassist 所强调的是源代码级别的工作
ASM
是一个轻量级 Java 字节码操作框架,直接涉及到JVM底层的操作和指令,高性能,高质量
CGLB
生成类库,基于ASM实现
javassist
是一个开源的分析、编辑和创建 Java 字节码的类库,性能较 ASM 差,跟 CGLIB 差不多,但是使用简单,很多开源框架都在使用它
Javassist 中最为重要的是 ClassPool、CtClass 、CtMethod、CtField
ClassPool:一个基于HashMap实现的 CtClass 对象容器,其中键是类名称,值是表示该类的 CtClass 对象,默认的ClassPool 使用与底层 JVM 相同的类路径,因此在某些情况下,可能需要向 ClassPool 添加类路径或类字节
CtClass:表示一个类,这些 CtClass 对象可以从 ClassPool 获得
CtMethods:表示类中的方法
CtFields :表示类中的字段
优势:
比反射开销小、性能高
操作字节码可以动态生成新的类,动态修改类(添加、删除、修改属性或方法)
javassist 外层 API 和反射类似
主要有 CtClass、CtMethod、CtField 组成,执行反射中的 java.lang.Class、java.lang.reflect.Method、 java.lang.reflect.Method .Field 中的操作
劣势:
不支持 JDK5 的新语法,包括泛型、枚举,不支持注解修改
不支持数组初始化
不支持内部类和匿名类
不支持 continue 和 break
动态创建类
public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException {
ClassPool pool = ClassPool.getDefault();
// 创建User
CtClass userClass = pool.makeClass("com.kernel.entity.User");
// 创建属性
CtField nameField = CtField.make("private String name;", userClass);
CtField ageField = CtField.make("private Integer age;", userClass);
// 添加属性
userClass.addField(nameField);
userClass.addField(ageField);
// 创建方法
CtMethod getName = CtMethod.make("public String getName() {return name;}", userClass);
CtMethod setName = CtMethod.make("public void setName(String name) {this.name = name;}", userClass);
// 添加方法
userClass.addMethod(getName);
userClass.addMethod(setName);
// 创建构造器
CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"), pool.get("java.lang.Integer")}, userClass);
// 设置内容
constructor.setBody("{this.name = name;this.age = age;}");
// 添加构造器
userClass.addConstructor(constructor);
userClass.writeFile("D:\Codes\Java\Performance\jvm-day03\src\main\java\com\kernel\test");
}
动态添加并执行方法
public static void main(String[] args) {
try {
ClassPool pool = ClassPool.getDefault();
// 读取com.kernel.User
CtClass userClass = pool.get("com.kernel.User");
// 创建方法,设置方法返回值
CtMethod method = new CtMethod(CtClass.voidType, "sum", new CtClass[] { CtClass.intType, CtClass.intType },
userClass);
// 设置方法内容
method.setBody("{System.out.println(\"sun:\" + ($1 + $2));}");
// 添加方法
userClass.addMethod(method);
userClass.writeFile("D:\Codes\Java\Performance\jvm-day03\src\main\java\com\kernel\test");
// 动态执行方法
Class clazz = userClass.toClass();
Object newInstance = clazz.newInstance();
// 获得方法
Method sumMethod = clazz.getDeclaredMethod("sum", int.class, int.class);
sumMethod.invoke(newInstance, 2, 5);
} catch (Exception e) {
e.printStackTrace();
}
}

以上是关于动态字节码技术 javassist 初探的主要内容,如果未能解决你的问题,请参考以下文章

字节码编程 | 使用Javassist动态生成Hello World

Java动态字节技术之Javassist

Java 虚拟机原理动态字节码技术 | Dalvik & ART 虚拟机 | Android 字节码打包过程

JAVAssist字节码操作

5.Dubbo原理解析-代理之Javassist字节码技术生成代理 (转)

字节码Javassist 通过字节码插桩监控方法采集运行时入参 出参和异常信息