javassist

javassist (Java编程助手)使Java字节码操作变得简单。

它是Java中编辑字节码的类库;它允许Java程序在运行时定义新类,并在JVM加载类文件时修改类文件。

与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源级和字节码级。

如果用户使用源代码级API,他们可以编辑类文件,而不需要了解Java字节码的规范。

整个API只使用Java语言的词汇表进行设计。您甚至可以以源文本的形式指定插入的字节码;Javassist动态编译它。

另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。

快速开始

jar 导入

  [xml]
1
2
3
4
5
<dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency>

代码实现

  • PathUtil.java
  [java]
1
2
3
4
5
6
7
8
9
10
11
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
  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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

  [java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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