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

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

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

    1. package com.carlzone.dubbo.javassist;
    2. import javassist.*;
    3. import java.io.File;
    4. import java.io.FileOutputStream;
    5. import java.lang.reflect.Modifier;
    6. public class CompilerByJavassist {
    7. public static void main(String[] args) throws Exception {
    8. // ClassPool: CtClass对象容器
    9. ClassPool pool = ClassPool.getDefault();
    10. // 通过ClassPool生成一个public的User类
    11. CtClass ctClass = pool.makeClass("com.carlzone.learn.javassist.User");
    12. // 添加属性
    13. // 1. 添加属性private int id;
    14. CtField idField = new CtField(pool.getCtClass("int"), "id", ctClass);
    15. idField.setModifiers(Modifier.PRIVATE);
    16. ctClass.addField(idField);
    17. // 2.添加属性private String username
    18. CtField nameField = new CtField(pool.get("java.lang.String"), "username", ctClass);
    19. nameField.setModifiers(Modifier.PRIVATE);
    20. ctClass.addField(nameField);
    21. // 添加setter/getter方法
    22. ctClass.addMethod(CtNewMethod.getter("getId", idField));
    23. ctClass.addMethod(CtNewMethod.setter("setId", idField));
    24. ctClass.addMethod(CtNewMethod.getter("getUsername", nameField));
    25. ctClass.addMethod(CtNewMethod.setter("setUsername", nameField));
    26. // 添加构造函数
    27. CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, ctClass);
    28. // 添加构造函数方法体
    29. StringBuffer sb = new StringBuffer();
    30. sb.append("{\n").append("this.id = 27;\n").append("this.username=\"carl\";\n}");
    31. ctConstructor.setBody(sb.toString());
    32. ctClass.addConstructor(ctConstructor);
    33. // 添加自定义方法
    34. CtMethod printMethod = new CtMethod(CtClass.voidType, "print", new CtClass[]{}, ctClass);
    35. printMethod.setModifiers(Modifier.PUBLIC);
    36. StringBuffer printSb = new StringBuffer();
    37. printSb.append("{\nSystem.out.println(\"begin!\");\n")
    38. .append("System.out.println(id);\n")
    39. .append("System.out.println(username);\n")
    40. .append("System.out.println(\"end!\");\n")
    41. .append("}");
    42. printMethod.setBody(printSb.toString());
    43. ctClass.addMethod(printMethod);
    44. // 生成一个Class
    45. Class<?> clazz = ctClass.toClass();
    46. Object obj = clazz.newInstance();
    47. // 反射执行方法
    48. obj.getClass().getMethod("print", new Class[]{}).invoke(obj, new Object[]{});
    49. // 把生成的class写入到文件中
    50. byte[] byteArr = ctClass.toBytecode();
    51. FileOutputStream fos = new FileOutputStream(new File("D://User.class"));
    52. fos.write(byteArr);
    53. fos.close();
    54. }
    55. }

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

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

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

    Java字节码框架 Javassist使用 - 图1

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

    Java字节码框架 Javassist使用 - 图2

    最后需要特别注意的是:

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

    参考文章/推荐阅读:

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