javassist
javassist (Java编程助手)使Java字节码操作变得简单。
它是Java中编辑字节码的类库;它允许Java程序在运行时定义新类,并在JVM加载类文件时修改类文件。
与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源级和字节码级。
如果用户使用源代码级API,他们可以编辑类文件,而不需要了解Java字节码的规范。
整个API只使用Java语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码;Javassist动态编译它。
另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。
快速开始
jar 导入
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency>
代码实现
- PathUtil.java
public class PathUtil {
/**
* 类似getPath(Class), 只是不包含类的路径,而是获取到当前类包的根路径。
* @param clazz 类
* @return 转换后的路径
*/
public static String getRootPath(Class clazz, final String classPath) {
String uriPath = clazz.getResource("/").toString();
return uriPath.replace("file:", "");
}
}
- Main.java
package com.github.houbb.spring.aop.javassist;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Modifier;
import javassist.NotFoundException;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
// 1. 创建一个空类
final String classPath = "com.github.houbb.spring.aop.javassist.GenClass";
CtClass cc = pool.makeClass(classPath);
// 2. 新增一个字段 private String name = "init";
// 字段名为name
CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
// 访问级别是 private
param.setModifiers(Modifier.PRIVATE);
// 初始值是 "init"
cc.addField(param, CtField.Initializer.constant("init"));
// 3. 生成 getter、setter 方法
cc.addMethod(CtNewMethod.setter("setName", param));
cc.addMethod(CtNewMethod.getter("getName", param));
// 4. 添加无参的构造函数
CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
cons.setBody("{name = \"ryo\";}");
cc.addConstructor(cons);
// 5. 添加有参的构造函数
// http://jboss-javassist.github.io/javassist/tutorial/tutorial2.html#before
cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
// $0=this / $1,$2,$3... 代表方法参数
cons.setBody("{$0.name = $1;}");
cc.addConstructor(cons);
// 6. 创建一个名为execute方法,无参数,无返回值,输出name值
CtMethod ctMethod = new CtMethod(CtClass.voidType, "execute", new CtClass[]{}, cc);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{System.out.println(name);}");
cc.addMethod(ctMethod);
final String targetPath = PathUtil.getRootPath(Main.class, classPath);
cc.writeFile(targetPath);
}
}
测试后生成的文件
~/target/classes/com/github/houbb/spring/aop/javassist/GenClass.class
package com.github.houbb.spring.aop.javassist;
public class GenClass {
private String name = "init";
public void setName(String var1) {
this.name = var1;
}
public String getName() {
return this.name;
}
public GenClass() {
this.name = "ryo";
}
public GenClass(String var1) {
this.name = var1;
}
public void execute() {
System.out.println(this.name);
}
}
参考资料
http://www.javassist.org/tutorial/tutorial.html
http://zhxing.iteye.com/blog/1703305