概述
Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果。熟练使用这套工具,可以让Java编程更接近与动态语言编程。
教程
maven依赖
pom.xml
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.20.0-GA</version>
</dependency>
API讲解
1、创建一个新的Class
ClassPool pool = ClassPool.getDefault();
//定义类
CtClass stuClass = pool.makeClass("com.ricky.Student");
当然,如果某个类已经存在,可以直接加载它,如下:
CtClass cc = pool.get("java.lang.String");
2、构造类成员变量
//id属性
CtField idField = new CtField(CtClass.longType, "id", stuClass);
stuClass.addField(idField);
3、构造类方法
CtMethod getMethod = CtNewMethod.make("public int getAge() { return this.age;}", stuClass);
CtMethod setMethod = CtNewMethod.make("public void setAge(int age) { this.age = age;}", stuClass);
stuClass.addMethod(getMethod);
stuClass.addMethod(setMethod);
应用示例
1、动态构造一个Class
package com.ricky.codelab.javassist;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
/**
* 动态构造Class
* @author Ricky
*
*/
public class JavassistDemo {
public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
//定义类
CtClass stuClass = pool.makeClass("com.ricky.Student");
//加载类
// CtClass cc = pool.get(classname);
//id属性
CtField idField = new CtField(CtClass.longType, "id", stuClass);
stuClass.addField(idField);
//name属性
CtField nameField = new CtField(pool.get("java.lang.String"), "name", stuClass);
stuClass.addField(nameField);
//age属性
CtField ageField = new CtField(CtClass.intType, "age", stuClass);
stuClass.addField(ageField);
CtMethod getMethod = CtNewMethod.make("public int getAge() { return this.age;}", stuClass);
CtMethod setMethod = CtNewMethod.make("public void setAge(int age) { this.age = age;}", stuClass);
stuClass.addMethod(getMethod);
stuClass.addMethod(setMethod);
// stuClass.writeFile("F:\\Practice_Demo");
Class<?> clazz = stuClass.toClass();
System.out.println("class:"+clazz.getName());
System.out.println("------------属性列表------------");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType()+"\t"+field.getName());
}
System.out.println("------------方法列表------------");
//方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method: methods){
System.out.println(method.getReturnType()+"\t"+method.getName()+"\t"+Arrays.toString(method.getParameterTypes()));
}
}
}
2、指定父类
package com.ricky.codelab.javassist;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
public class JavassistExtendDemo {
public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
//定义类
CtClass stuClass = pool.makeClass("com.ricky.Student");
//设置父类
stuClass.setSuperclass(pool.get("com.ricky.codelab.javassist.domain.Person"));
//hobbies属性
CtField ageField = new CtField(pool.getCtClass("java.util.List"), "hobbies", stuClass);
stuClass.addField(ageField);
Class<?> clazz = stuClass.toClass();
System.out.println("class:"+clazz.getName());
System.out.println("------------属性列表------------");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType()+"\t"+field.getName());
}
System.out.println("------------方法列表------------");
//方法
Method[] methods = clazz.getMethods();
for (Method method: methods){
System.out.println(method.getReturnType()+"\t"+method.getName()+"\t"+Arrays.toString(method.getParameterTypes()));
}
}
}
3、动态注入代码
package com.ricky.codelab.javassist;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;
/**
* 动态注入代码
*
* @author Ricky
*
*/
public class JavassistInsertDemo {
public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException, InstantiationException, IllegalAccessException {
ClassPool pool = ClassPool.getDefault();
// 定义类
CtClass ctClass = pool.get("com.ricky.codelab.javassist.Calculator");
// 需要修改的方法名称
String mname = "getSum";
CtMethod mold = ctClass.getDeclaredMethod(mname);
// 修改原有的方法名称
String nname = mname + "$impl";
mold.setName(nname);
//创建新的方法,复制原来的方法
CtMethod mnew = CtNewMethod.copy(mold, mname, ctClass, null);
// 主要的注入代码
StringBuffer body = new StringBuffer();
body.append("{\nlong start = System.currentTimeMillis();\n");
// 调用原有代码,类似于method();($$)表示所有的参数
body.append(nname + "($$);\n");
body.append("System.out.println(\"Call to method " + mname
+ " took \" +\n (System.currentTimeMillis()-start) + " + "\" ms.\");\n");
body.append("}");
// 替换新方法
mnew.setBody(body.toString());
// 增加新方法
ctClass.addMethod(mnew);
Calculator calculator =(Calculator)ctClass.toClass().newInstance();
calculator.getSum(10000);
}
}
class Calculator {
public void getSum(long n) {
long sum = 0;
for (int i = 0; i < n; i++) {
sum += i;
}
System.out.println("n="+n+",sum="+sum);
}
}