概述
java动态字节码指的是在java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对代码的二进制文件进行修改。动态java字节码主要是为了减少冗余代码,提高性能。
实现字节码增强的主要步骤:
- 修改字节码。在内存中获取到原来的字节码,通过一些工具(如
ASM
、Javaasist
)来修改它的byte[]数组,得到一个新的byte[]数组。 - 使修改后的字节码生效
- 自定义
ClassLoader
来加载修改后的字节码 - 替换掉原来的字节码,在
JVM
加载用户的Class
时,拦截返回修改后的字节码。
- 自定义
用途
- 动态生成新的类
- 动态修改某个类的结构(添加/删除/修改 新的属性/方法)
- AOP技术使用的就是动态字节码技术
字节码操作类库
BCEL
BCEL
可以深入JVM
汇编语言进行类操作的细节。BCEL
与javassist
有不同的处理字节码方法,BCEL
在实际的JVM
指令层次上进行操作,而javassist
所强调的是源代码级别的工作。
ASM
是轻量级java
字节码操作框架,直接涉及到JVM
底层的操作和指令。高性能,高质量
CGLB
基于ASM
实现
javassist
性能低于ASM
跟CGLB
差不多,但是使用简单,很多开源框架都使用的是javassist
。javassist
比反射开销小,性能高。
javassist
的最外层的API
和JAVA
的反射包中的API
颇为相似。它主要由CtClass
、CtMethod
以及CtField
几个类组成,用以执行和JDK
反射API
中java.lang.Class
、java.lang.reflect.Method
、java.lang.reflect.Field
相同的操作。
局限性
JDK5.0
新语法不支持(包括泛型、枚举),不支持注解修改- 不支持数组的初始化
- 不支持内部类和匿名类
- 不支持
continue
和break
表达式 - 对于继承关系,有些不支持。
maven : mvnrepository.com/artifact/ja…
反射调用方法
public class User {
private String name;
private Integer age;
public void sum(int a,int b){
int sum = a + b;
System.out.println("sum = " + sum);
}
public static void main(String[] args) {
try {
Class<?> clz = Class.forName("javassist.User");
Object newInstance = clz.newInstance();
Method method = clz.getDeclaredMethod("sum", int.class, int.class);
Object invoke = method.invoke(newInstance, 1, 3);
}catch (Exception e){
}
}
}
复制代码
javassist
生成class
文件
//使用javassist创建class文件
ClassPool pool = ClassPool.getDefault();
//创建 class 文件
CtClass userClass = pool.makeClass("com.beisiji.javassist.User");
//创建 id 属性
CtField idField = CtField.make("private Integer id;", userClass);
//创建 name 属性
CtField nameField = CtField.make("private String name;", userClass);
//添加属性
userClass.addField(idField);
userClass.addField(nameField);
//创建方法
CtMethod setIdMethod = CtMethod.make("public void setId(Integer id) { this.id = id; }", userClass);
CtMethod getIdMethod = CtMethod.make("public Integer getId() { return id; }", userClass);
//添加方法
userClass.addMethod(setIdMethod);
userClass.addMethod(getIdMethod);
//创建构造器
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, userClass);
ctConstructor.setBody("{ this.id = id;this.name = name; }");
userClass.addConstructor(ctConstructor);
userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
复制代码
javassist
修改类文件信息
//使用javassist修改类文件信息(添加方法)
ClassPool pool = ClassPool.getDefault();
//需要加载的类信息(需要修改类信息的全限定名称)
CtClass userClass = pool.get("executor.ExecutorDemo");
//创建方法
CtMethod method = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, userClass);
//设置方法权限
method.setModifiers(Modifier.PUBLIC);
method.setBody("{return $1 + $2;}"); //$0:this , $1第一个参数 , $2第二个参数
userClass.addMethod(method);
userClass.writeFile("C:/Users/yuanl/Desktop/md_dir");
//调用添加的方法
Class clz = userClass.toClass();
Object newInstance = clz.newInstance();
Method addMethod = clz.getDeclaredMethod("add", int.class, int.class);
Object invoke = addMethod.invoke(newInstance, 2, 3);
System.out.println("invoke = " + invoke);
复制代码