javaassist操作字节码
java动态性常见的两种实现方式
-
字节码操作
-
反射
运行时操作字节码可以让我们实现如下功能
-
动态生成新的类
-
动态改变某个类的结构(添加/删除/修改 新的属性/方法)
优势
-
比反射开销小
-
JAVAasist性能高于反射,低于ASM
常见的字节码操作类库
-
BCEL
-
ASM
-
CGLIB
-
Javaassist
-
javaassist的最外层API跟JAVA反射包中的API颇为相似
-
它主要由CtClass、CtMethod以及CtField几个类组成。用以执行和JDK反射API中java.lang.Class、java.lang.reflect.Method、java.lang.reflect.Method.Field相同的操作
利用javaassist生成一个类
public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException {
// 获取javaassist类池
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("com.eric.bean.Emp");
// 创建属性
CtField f1 = CtField.make("private int empno;", ctClass);
CtField f2 = CtField.make("private String empname;", ctClass);
ctClass.addField(f1);
ctClass.addField(f2);
// 创建方法
CtMethod m1 = CtMethod.make("public int getEmpno() {return empno;}", ctClass);
CtMethod m2 = CtMethod.make("public void setEmpno(int empno) {this.empno = empno;}", ctClass);
CtMethod m3 = CtMethod.make("public String getEmpname() {return empname;}", ctClass);
CtMethod m4 = CtMethod.make("public void setEmpname(String empname) {this.empname = empname;}", ctClass);
ctClass.addMethod(m1);
ctClass.addMethod(m2);
ctClass.addMethod(m3);
ctClass.addMethod(m4);
// 添加构造器
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, ctClass);
ctConstructor.setBody("{this.empno = empno; this.empname = empname;}");
ctClass.addConstructor(ctConstructor);
// 将上面构造好的类写入d:/myjava中
ctClass.writeFile("d:/myjava");
System.out.println("生成类成功!");
}
javaassist详细操作
ClassPool pool = ClassPool.getDefault();
// 获取类
CtClass emp = pool.get("com.eric.test.Emp");
// 打印字节码信息
byte[] bytes = emp.toBytecode();
System.out.println(Arrays.toString(bytes));
System.out.println(emp.getName()); // 获取类名
System.out.println(emp.getSimpleName()); // 获取简要类名
System.out.println(emp.getSuperclass()); // 获取父类
System.out.println(emp.getInterfaces()); // 获取接口
javaassist操作方法需要注意几个占位符
-
$0:表示this关键字
-
$1:表示第一个形参
-
$2:表示第二个形参,$3、$4以此类推
-
$args:所有参数的数组形式
-
$$:所有参数的简写,即move($$)等价于move($1, $2),方便调用
-
$cflow:一个方法调用的深度,例如在递归调用中显示
-
$r:方法的返回值类型
-
$_:方法的返回值,在修改方法体时不支持
-
addCatch():增加try catch块,$e代表异常对象
-
$class:this的类型,也就是$0的类型
-
$sig:方法参数类型的数组
为一个类生成新的方法
public static void testMethod () throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("com.eric.test.Emp");
// 声明一个方法
// 方式一
CtMethod ctMethod = CtMethod.make("public int minus(int a, int b) {return a - b;}", ctClass);
ctClass.addMethod(ctMethod);
// 方式二
CtMethod m = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, ctClass);
m.setModifiers(Modifier.PUBLIC); // 声明修饰符
m.setBody("{return $1 + $2;}");
ctClass.addMethod(m);
// 通过反射调用新生成的方法
Class<?> aClass = ctClass.toClass();
Object o = aClass.newInstance(); // 通过调用emp的无参构造器生成对象
Method add = aClass.getDeclaredMethod("add", int.class, int.class);
Object result = add.invoke(o, 200, 300);
System.out.println(result);
}
public static void testMethod1 () throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("com.eric.test.Emp");
CtMethod sayHello = ctClass.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});
// 在方法前面添加一些代码
sayHello.insertBefore("System.out.println($1); System.out.println(\"start\");");
// 在方法指定行添加一些代码
sayHello.insertAfter("System.out.println(\"end\");");
// 在方法后面添加一些代码
sayHello.insertAt(44, "System.out.println(\"insertAt\");");
// 反射调用
Class<?> aClass = ctClass.toClass();
Object o = aClass.newInstance();
Method sayHello1 = aClass.getDeclaredMethod("sayHello", int.class);
sayHello1.invoke(o, 100);
}
public static void testField() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("com.eric.test.Emp");
// 创建属性
// 方式一
CtField f1 = CtField.make("private int empId;", ctClass);
ctClass.addField(f1);
// 方式二
CtField f2 = new CtField(CtClass.intType, "salary", ctClass);
f1.setModifiers(Modifier.PRIVATE);
ctClass.addField(f2, "100"); // 传入默认值
// 获取指定属性
// ctClass.getDeclaredField("empname");
// 生成setter跟getter方法
ctClass.addMethod(CtNewMethod.getter("getSalary", f2));
ctClass.addMethod(CtNewMethod.setter("setSalary", f2));
}
public static void testConstructor() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("com.eric.test.Emp");
CtConstructor[] cs = ctClass.getDeclaredConstructors();
for (CtConstructor c: cs) {
System.out.println(c.getName());
c.insertBefore(""); // 在构造器前面加代码 以及后面等等 同方法操作
}
}
public @interface Author {
String name();
int year();
}
public static void testAnnotation() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("com.eric.test.Emp");
Object[] annotations = ctClass.getAnnotations();
Author author = (Author) annotations[0];
System.out.println(author.name());
System.out.println(author.year());
}
-
-
不支持数组的初始刷,如String[]{"1", "2"},除非只有容量为1的数组
-
不支持内部类和匿名类
-
不支持continue和break表达式
-

浙公网安备 33010602011771号