专业的JAVA编程教程与资源

网站首页 > java教程 正文

一文了解java字节码操作——javassist

temp10 2024-09-19 03:57:35 java教程 12 ℃ 0 评论

javassist全称(Java Programming Assistant),是一款比JDK内置库操作字节码更方便的代码库。为了方便实验,先引入它的依赖

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.21.0-GA</version>
</dependency>

什么是字节码呢?字节码就是一套可以被JVM处理的指令集,JVM将字节码指令转换为机器级汇编指令。举例如下:

一文了解java字节码操作——javassist

创建一个User类,并用javac编译后得到User.class

public class User {
    private String name;
    private int age;

    public void baseinfo(String name, int age){
        this.name = name;
        this.age = age;
    }
}

然后 javap -c User.class 输出下面的字节码(java version "1.8.0_181")

public class bluesky.javassist.User {
  public bluesky.javassist.User();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void baseinfo(java.lang.String, int);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #2                  // Field name:Ljava/lang/String;
       5: aload_0
       6: iload_2
       7: putfield      #3                  // Field age:I
      10: return
}

aload_0指令将this指针入栈,aload_1将name变量入栈,putfield是实例字段访问指令,此处就是设置name字段。javassist可以帮助我们非常方便的操作这些字节码。

使用javassist生成一个类:

// 创建一个ClassFile对象
ClassFile cf = new ClassFile(false, "bluesky.javassist.JavassistGeneratedClass", null);
// 设置JavassistGeneratedClass实现Cloneable接口
cf.setInterfaces(new String[] {"java.lang.Cloneable"});
// 添加id属性
FieldInfo f = new FieldInfo(cf.getConstPool(), "id", "I");
// 属性的修饰符
f.setAccessFlags(AccessFlag.PUBLIC);
cf.addField(f);
ClassPool classPool = ClassPool.getDefault();
// 生成JavassistGeneratedClass类并获取它的所有属性
Field[] fields = classPool.makeClass(cf).toClass().getFields();
 // 断言是否存在id属性
assertEquals(fields[0].getName(), "id");

获取方法baseinfo的指令:

ClassPool cp = ClassPool.getDefault();
ClassFile cf = cp.get("bluesky.javassist.User")
  .getClassFile();
MethodInfo minfo = cf.getMethod("baseinfo");
CodeAttribute ca = minfo.getCodeAttribute();
CodeIterator ci = ca.iterator();

List<String> operations = new LinkedList<>();
while (ci.hasNext()) {
    int index = ci.next();
    int op = ci.byteAt(index);
    operations.add(Mnemonic.OPCODE[op]);
}

assertEquals(operations,
  Arrays.asList(
  "aload_0", 
  "aload_1", 
  "putfield", 
  "aload_0", 
  "iload_2",  
  "putfield", 
  "return"));

添加构造方法:

ClassFile cf = ClassPool.getDefault().get("bluesky.javassist.User").getClassFile();
Bytecode code = new Bytecode(cf.getConstPool());
code.addAload(0);
code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
code.addReturn(null);

MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
minfo.setCodeAttribute(code.toCodeAttribute());
cf.addMethod(minfo);

在方法指定为止插入自己需要的逻辑:

        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.get("bluesky.javassist.User");
        CtMethod declaredMethod = ctClass.getDeclaredMethod("baseinfo");
        declaredMethod.insertBefore("System.out.println(\"方法执行的第一行\")");
        declaredMethod.insertAfter("System.out.println(\"方法return前\")");
        User instance = (User) ctClass.toClass().newInstance();
        instance.baseinfo("hello", 1);

通常可以使用字节码技术实现动态代理、热加载、debug、结合agent实现应用指标监控等。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表