Java字节码框架 -- Javassist

时间:2021-09-28 14:09:00

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态”AOP”框架。

关于java字节码的处理,目前有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

下面我们就用使用Javassist来创建一个类,并调用其中的方法

package com.carlzone.dubbo.javassist;

import javassist.*;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Modifier;

public class CompilerByJavassist {

public static void main(String[] args) throws Exception {
// ClassPool: CtClass对象容器
ClassPool pool = ClassPool.getDefault();

// 通过ClassPool生成一个public的User类
CtClass ctClass = pool.makeClass("com.carlzone.learn.javassist.User");


// 添加属性
// 1. 添加属性private int id;
CtField idField = new CtField(pool.getCtClass("int"), "id", ctClass);
idField.setModifiers(Modifier.PRIVATE);
ctClass.addField(idField);

// 2.添加属性private String username
CtField nameField = new CtField(pool.get("java.lang.String"), "username", ctClass);
nameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(nameField);

// 添加setter/getter方法
ctClass.addMethod(CtNewMethod.getter("getId", idField));
ctClass.addMethod(CtNewMethod.setter("setId", idField));
ctClass.addMethod(CtNewMethod.getter("getUsername", nameField));
ctClass.addMethod(CtNewMethod.setter("setUsername", nameField));

// 添加构造函数
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, ctClass);
// 添加构造函数方法体
StringBuffer sb = new StringBuffer();
sb.append("{\n").append("this.id = 27;\n").append("this.username=\"carl\";\n}");
ctConstructor.setBody(sb.toString());
ctClass.addConstructor(ctConstructor);

// 添加自定义方法
CtMethod printMethod = new CtMethod(CtClass.voidType, "print", new CtClass[]{}, ctClass);
printMethod.setModifiers(Modifier.PUBLIC);
StringBuffer printSb = new StringBuffer();
printSb.append("{\nSystem.out.println(\"begin!\");\n")
.append("System.out.println(id);\n")
.append("System.out.println(username);\n")
.append("System.out.println(\"end!\");\n")
.append("}");
printMethod.setBody(printSb.toString());
ctClass.addMethod(printMethod);

// 生成一个Class
Class<?> clazz = ctClass.toClass();
Object obj = clazz.newInstance();

// 反射执行方法
obj.getClass().getMethod("print", new Class[]{}).invoke(obj, new Object[]{});

// 把生成的class写入到文件中
byte[] byteArr = ctClass.toBytecode();
FileOutputStream fos = new FileOutputStream(new File("D://User.class"));
fos.write(byteArr);
fos.close();

}

}

我们来看一下创建这个对象的全部过程:

  • 声明对象的全类名
  • 创建field,并设置getter/setter方法
  • 创建对象的无参构造器
  • 创建自定义方法

其实这个过程就相当于我们手写了这样一个类,下面我们来运行一下这个类:

Java字节码框架 -- Javassist

下面我们到D盘去用反编译工具看一下生成的Class文件。

Java字节码框架 -- Javassist

最后需要特别注意的是:

  • Javassist不支持要创建或注入的类中存在泛型参数
  • Javassist对@类型的注解(Annotation)只支持查询,不支持添加或修改

参考文章/推荐阅读:

  1. 用 Javassist 进行类转换
  2. Javassist的官方网站
  3. javassist 学习笔记
  4. javassist学习
  5. Javassist学习总结