操控字节码:Javaassist介绍

欢迎关注微信公众号:互联网全栈架构

Javaassist(JAVA programming ASSISTant),是一个可以操控字节码的类库,能够在不修改源代码的情况下,在运行时动态地对类的结构和方法进行拓展(当然也可以新增类)。它提供了丰富的API,让开发人员可以方便地对字节码进行操控。下面我们用实例来展示它的使用。创作不易,辛苦在文末点个赞,谢谢!

首先初步认识一下字节码,我们知道,Java源代码在编译后生成了class文件,也就是字节码文件,JVM加载class文件后再翻译成对应平台的指令。定义一下简单的类,看看生成的字节码文件是什么样子,有一个初步的直观感受:

package com.sample.core.bytecode;
public class Calculator {
    public int sum(int x, int y){
        System.out.println("Sum Operation");
        return x+y;
    }
}

编译后生成了.class文件,使用javap命令查看文件的字节码:

javap -c Calculator.class
public class com.sample.core.bytecode.Calculator {
  public com.sample.core.bytecode.Calculator();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4return

  public int sum(intint);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Sum Operation
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: iload_1
       9: iload_2
      10: iadd
      11: ireturn
}

以上就是对应的字节码,而Javaassist提供了丰富的API,可以操控字节码文件。对于这些字节码指令的具体含义,可以参考具体的资料说明,这里不做说明。我们来看看Javaassist的一些主要功能,比如新增一个类、加载已有类并修改、新增字段和方法等。首先在POM文件中引入依赖:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.28.0-GA</version>
</dependency>
一、生成一个新类

Javaassist可以新生成一个类,比如我们想新生成一个类ManuNewClass,它实现接口java.io.Serializable,并且有一个名为id的成员变量,ClassFile用于定义一个新类,FieldInfo用于新增一个成员变量,CtClass是类的抽象表示形式:

// 新增一个类,并实现接口
ClassFile cf = new ClassFile(
        false"com.sample.core.bytecode.ManuNewClass"null);
cf.setInterfaces(new String[] {"java.io.Serializable"});
// 增加新的成员变量
FieldInfo f = new FieldInfo(cf.getConstPool(), "id""id");
f.setAccessFlags(AccessFlag.PUBLIC);
cf.addField(f);
// 生成新类
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(cf);
二、修改现有类

比如我们之前定义的类Calculator,现在修改它的父类为com.sample.core.bytecode.ParentClass:

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("com.sample.core.bytecode.Calculator");
ctClass.setSuperclass(classPool.get("com.sample.core.bytecode.ParentClass"));
三、新增成员变量

在之前定义的计算器类中新增成员变量id:

ClassPool classPool = ClassPool.getDefault();
ClassFile classFile = classPool.get("com.sample.core.bytecode.Calculator").getClassFile();
FieldInfo fieldInfo = new FieldInfo(classFile.getConstPool(), "id""id");
fieldInfo.setAccessFlags(AccessFlag.PUBLIC);
classFile.addField(fieldInfo);
四、新增成员方法

比如我们要新增一个test方法,它打印Hello World:

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get("com.sample.core.bytecode.Calculator");
CtMethod ctMethod =CtNewMethod.make("public void test() { System.out.println("Hello World");}", ctClass);
ctClass.addMethod(ctMethod);
五、示例

我们以常见的AOP为例,比如我们要在方法调用之前做一些操作,在方法调用之后再做别的操作,类似于这样的功能,用Javaassist是可以实现的,对于我们在文章开头定义的计算器类,如果我们需要在计算之前打印信息,在计算完成后也要给出提示,在不修改源代码的情况下,实现方法如下:

package com.sample.core.bytecode;

import javassist.*;
public class JavaassistExample {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass targetClass = pool.get("com.sample.core.bytecode.Calculator");
        CtMethod targetMethod = targetClass.getDeclaredMethod("sum");
        // 在方法调用前打印信息
        targetMethod.insertBefore("{ System.out.println("Start Calculating:"); }");
        // 在方法调用后打印信息
        targetMethod.insertAfter("{ System.out.println("End Calculating:"); }");
        Class c = targetClass.toClass();
        Calculator calculator = (Calculator) c.newInstance();
        calculator.sum(57);
    }
}

运行上面的程序,在控制台中打印出如下信息,可以看出,在调用方法sum前后,都打印出了对应的信息:

Start Calculating:
Sum Operation
End Calculating:
六、总结

Javaassist可以对字节码进行操作,所以对于AOP、动态代理、安全检测、链路跟踪等方面都能够广泛应用,相比于ASM,它提供了更为友好和便捷的API,但在性能要求较为苛刻的场合,ASM更胜一筹。

以上就是关于字节码增强的类库Javaassist介绍,谢谢!

都看到这里了,请帮忙一键三连啊,也就是点击文末的在看、点赞、分享,这样会让我的文章让更多人看到,也会大大地激励我进行更多的输出,谢谢!

鸣谢:
https://github.com/jboss-javassist/javassist
https://www.51cto.com/article/750294.html


推荐阅读:

Spring Boot Starter原理及实践

聊聊MySQL中的死锁

越俎代庖:应用广泛的代理模式

MySQL整数类型的长度到底是什么含义?

漫谈MySQL中的事务

臭名昭著,怙恶不悛的OOM,到底是什么?

原文始发于微信公众号(互联网全栈架构):操控字节码:Javaassist介绍

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/173532.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!