ASM

ASM:

ASM是什么:简而言之,是一个用于操作java字节码操作框架,用于对源文件编译后的.class文件进行操作。

ASM核心概念:

ASM的设计核心是访问者模式,主要包含下面几个类:

  1. ClassReader
    用于读取和解析.class文件内容。
  2. ClassWriter
    用于生成或修改.class文件内容。
  3. ClassVisitor
    提供一种访问字节码元素(如类、方法、字段等)的回调接口。
  4. MethodVisitor
    用于访问方法字节码的指令序列。
  5. Opcodes
    定义了JVM指令集和相关常量。

ASM以事件驱动的方式操作字节码。当解析或生成字节码时,ASM将依次触发一系列回调方法,例如类加载、方法进入、方法字节码访问等,开发者可以在这些回调中实现自己的逻辑。

访问者模式:

访问者模式(Visitor Pattern)是一种 行为型设计模式,它的主要目的是在不修改对象结构的前提下,定义新的操作。这种模式适用于对象结构比较稳定,但需要对其元素定义多种操作的场景。

访问者模式将数据结构和作用于数据结构的操作解耦,使得新增操作更加灵活。

元素(Element):

      数据结构中的每个对象(元素)都接受访问者的访问,通常提供一个 accept 方法供访问者调用。

访问者(Visitor):

      定义一系列对数据结构中的元素执行的操作,每种操作是访问者的一个方法。

双分派机制

访问者模式依赖于双分派,即:

  • 元素通过调用访问者的方法,确定具体操作。
  • 访问者根据具体的元素类型,执行特定逻辑。
Visitor(访问者接口):

声明针对每种具体元素的访问方法,例如 visitElementA() 和 visitElementB()

ConcreteVisitor(具体访问者)

实现访问者接口,定义访问每种元素的具体操作。

Element(元素接口):

声明 accept 方法,接受访问者。

ConcreteElement(具体元素):

实现元素接口,调用访问者的 visit 方法。

ObjectStructure(对象结构):

维护一个元素集合,提供访问者访问这些元素的入口。

例:
// 访问者接口
interface Visitor {
    void visit(File file);
    void visit(Folder folder);
}

// 元素接口
interface Element {
    void accept(Visitor visitor);
}

// 文件类
class File implements Element {
    private String name;
    private int size;

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public String getName() {
        return name;
    }

    public int getSize() {
        return size;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this); // 双分派:把自身传递给访问者
    }
}

// 文件夹类
class Folder implements Element {
    private String name;
    private List<Element> elements = new ArrayList<>();

    public Folder(String name) {
        this.name = name;
    }

    public void addElement(Element element) {
        elements.add(element);
    }

    public List<Element> getElements() {
        return elements;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this); // 双分派:把自身传递给访问者
        for (Element element : elements) {
            element.accept(visitor); // 递归访问子元素
        }
    }
}

// 具体访问者:统计文件大小
class SizeCalculator implements Visitor {
    private int totalSize = 0;

    @Override
    public void visit(File file) {
        totalSize += file.getSize();
    }

    @Override
    public void visit(Folder folder) {
        // 文件夹本身不占大小,只访问子元素
    }

    public int getTotalSize() {
        return totalSize;
    }
}

// 具体访问者:统计文件和文件夹数量
class CountCalculator implements Visitor {
    private int fileCount = 0;
    private int folderCount = 0;

    @Override
    public void visit(File file) {
        fileCount++;
    }

    @Override
    public void visit(Folder folder) {
        folderCount++;
    }

    public int getFileCount() {
        return fileCount;
    }

    public int getFolderCount() {
        return folderCount;
    }
}

// 测试代码
public class VisitorPatternDemo {
    public static void main(String[] args) {
        // 创建文件和文件夹
        File file1 = new File("File1.txt", 100);
        File file2 = new File("File2.txt", 200);
        Folder folder = new Folder("MyFolder");
        folder.addElement(file1);
        folder.addElement(file2);

        // 使用访问者1:计算总大小
        SizeCalculator sizeCalculator = new SizeCalculator();
        folder.accept(sizeCalculator);
        System.out.println("Total size: " + sizeCalculator.getTotalSize());

        // 使用访问者2:统计数量
        CountCalculator countCalculator = new CountCalculator();
        folder.accept(countCalculator);
        System.out.println("Files: " + countCalculator.getFileCount());
        System.out.println("Folders: " + countCalculator.getFolderCount());
    }
}

访问者模式的优点

  1. 增加功能更方便:可以在不修改元素类的前提下,为其新增操作(只需增加新的访问者)。
  2. 解耦结构与操作:数据结构(如文件、文件夹)与操作(如计算大小、统计数量)分离,便于维护和扩展。

访问者模式的缺点

  1. 元素变动难:如果需要修改数据结构(例如新增一种元素类型),则需要修改所有访问者。
  2. 增加复杂性:访问者模式适用于结构稳定的场景,不适合频繁变动的结构。

访问者模式的应用场景

  1. 编译器
    • 分析语法树(AST)中的节点。
  2. 数据结构操作
    • 针对复杂的对象结构(如文件系统)定义多种操作。
  3. 跨对象操作
    • 在不破坏对象封装的前提下,实现跨类或跨结构的操作。

使用ASM:

启动:
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.3</version>
</dependency> 

//这段 XML 代码块是专门给 Maven 构建工具看的。
//你需要把它放在你 Java 项目根目录下的 pom.xml 文件中。
//找到 <dependencies> 标签(如果没有,就在 <project> 标签内创建一个)。
//把这段代码粘贴在 <dependencies> 和 </dependencies> 之间。
通常处理流程:

目标类 class bytes->ClassReader 解析->ClassVisitor 增强修改字节码->ClassWriter 生成增强后的 class bytes->通过 Instrumentation 解析加载为新的 Class

常用类和方法:

ClassVisitor:

用来“访问”一个 .class 的结构,并且可以在访问过程中读取、过滤、修改这个类的字节码信息。ClassVisitor 不是业务层面的“访问者模式示例类”,而是 ASM 对 Java class 文件做遍历/转换时的访问入口ASM 在处理 .class 文件时,不是让你一次性拿到完整类对象再改,而是像“事件流”一样,边读边把类中的各个部分通知给 ClassVisitor
ClassVisitor 收到这些通知后,可以选择:

  • 只看不改

  • 过滤掉一部分

  • 修改一部分

  • 转交给下一个 ClassVisitor

所以它被说成“事件筛选器”。

方法访问顺序:

visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd 
//?表示最多一个,*表示任意个

相关方法:

version:表示类文件的 JDK 版本
access:表示类的访问权限和属性
name:类的内部名称,用斜线代替点分隔包名和类名
signature:类的泛型签名,如果类没有泛型信息,此参数为 null
superName:父类的内部名称
interfaces:类实现的接口的内部名称数组。如果类没有实现任何接口,此参数为空数组

public abstract class ClassVisitor {
    // 构造方法
    public ClassVisitor(int api);
    public ClassVisitor(int api, ClassVisitor cv);
    // 访问类的基本信息。version参数表示类的版本号,access参数表示类的访问标志,name参数表示类的内部名称,signature参数表示类的泛型签名(如果适用),superName参数表示父类的内部名称,interfaces参数表示类实现的接口的内部名称数组
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
    // 访问源文件和调试信息。source参数表示源文件的名称,debug参数表示调试信息
    public void visitSource(String source, String debug);
    // 访问外部类信息。owner参数表示外部类的内部名称,name参数表示外部类的名称,desc参数表示外部类的描述符
    public void visitOuterClass(String owner, String name, String desc);
    // 访问类的注解,返回一个AnnotationVisitor实例,用于访问注解的内容。desc参数表示注解的描述符,visible参数表示注解是否在运行时可见
    AnnotationVisitor visitAnnotation(String desc, boolean visible);
    // 访问类的属性(Attribute),例如SourceFile属性。attr参数表示要访问的属性
    public void visitAttribute(Attribute attr);
    // 访问内部类信息。name参数表示内部类的内部名称,outerName参数表示内部类的外部类的内部名称,innerName参数表示内部类的名称,access参数表示内部类的访问标志
    public void visitInnerClass(String name, String outerName, String innerName, int access);
    // 访问类的字段。access参数表示字段的访问标志,name参数表示字段的名称,desc参数表示字段的描述符,signature参数表示字段的泛型签名(如果适用),value参数表示字段的初始值
    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value);
    // 访问类的方法。access参数表示方法的访问标志,name参数表示方法的名称,desc参数表示方法的描述符,signature参数表示方法的泛型签名(如果适用),exceptions参数表示方法声明的异常类型的内部名称数组
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);
    // 访问类的结束,表示不再访问该类的任何内容
    void visitEnd();
}

实例:

//实验类:bytecodeTest
package bytecode;
public class bytecodeTest extends Person implements helloInterface{
    private String sex;
    public bytecodeTest(String name, int age, String sex) {
        super(name, age);
        this.sex = sex;
    }
    public void sayHello() {
        System.out.println("Hello" + super.name);
    }
} 

//访问者类:MyClassVisitor
package javaasm;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class MyClassVisitor extends ClassVisitor {
    // 调用父类构造方法,使用ASM Opcodes版本
    public MyClassVisitor() {
        super(Opcodes.ASM5);
    }

    // 重写visit方法,输出类名
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // print class name
        System.out.println("The class name:" + name);
        super.visit(version, access, name, signature, superName, interfaces);
    }

    // 重写visitMethod方法,输出方法名
    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        // print method name
        System.out.println("The method name:" + name);
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }
} 

//结果:
The class name:bytecode/bytecodeTest
The method name:<init>
//构造方法名,在 JVM class 文件里,构造器并不直接叫类名,而是统一叫:<init>
The method name:sayHello

Process finished with exit code 0

ClassReader:

该类解析 ClassFile 内容,并针对遇到的每个字段、方法和字节码指令调用给定 ClassVisitor 的相应访问方法。这个类可以看作一个事件生产者

方法:

构造方法:
public ClassReader(byte[] classFile)
// classFile - the JVMS ClassFile structure to be read. 

public void accept(ClassVisitor classVisitor, int parsingOptions)
// classVisitor - the visitor that must visit this class.
// parsingOptions - the options to use to parse this class. One or more of SKIP_CODE, SKIP_DEBUG,SKIP_FRAMES or //EXPAND_FRAMES.

实例:

// 从文件系统中加载字节码
byte[] bytecode = Files.readAllBytes(Paths.get("path/to/MyClass.class"));
// 或者
 FileInputStream bytecode = new FileInputStream("path/to/MyClass.class");

// 从类加载器中加载字节码
InputStream is = getClass().getClassLoader().getResourceAsStream("com/example/MyClass.class");
byte[] bytecode = is.readAllBytes();

// 创建ClassReader实例
ClassReader classReader = new ClassReader(bytecode);

ClassWriter:

ClassWriter 类是 ClassVisitor 抽象类的一个子类,它直接以二进制形式生成编译后的类。它会生成一个字节数组形式的输出,其中包含了已编译类,可以用 toByteArray 方法来提取。这个类可以看作一个事件消费者

方法:

构造方法:
public ClassWriter(int flags)
// Constructs a new ClassWriter object.
public ClassWriter(ClassReader classReader, int flags)
// Constructs a new ClassWriter object and enables optimizations for "mostly add" bytecode transformations. 

public byte[] toByteArray()
// Returns the content of the class file that was built by this ClassWriter.
// Returns:
// the binary content of the JVMS ClassFile structure that was built by this ClassWriter.

MethodVisitor:

访问Java方法的访问者类,它由 ClassVisitorvisitMethod 方法返回。它可以看作“方法内部字节码事件的处理器/过滤器”。当 ClassVisitor 访问到一个方法时,会调用 visitMethod(...),这个 visitMethod(...) 会返回一个 MethodVisitor。然后,ASM 就会继续把这个方法里的内容,一步一步回调给这个 MethodVisitor

方法访问顺序:

visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )*
( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn | visitLocalVariable | visitLineNumber )*
visitMaxs )?
visitEnd 
//对非抽象方法,如果存在注解和属性,必须先访问;其次是按顺序访问字节代码,这些访问在visitCode与visitMaxs之间

相关方法:

abstract class MethodVisitor { // public accessors ommited
   // 构造方法
   MethodVisitor(int api);
   MethodVisitor(int api, MethodVisitor mv);

   // 访问方法的注解默认值
   AnnotationVisitor visitAnnotationDefault();
   // 访问方法的注解
   AnnotationVisitor visitAnnotation(String desc, boolean visible);
   // 访问方法参数的注解
   AnnotationVisitor visitParameterAnnotation(int parameter,
   String desc, boolean visible);
   // 访问方法的属性
   void visitAttribute(Attribute attr);
   // 访问方法的字节码指令部分
   void visitCode();

   // 访问方法的帧(Frame)。type参数表示帧的类型,nLocal参数表示局部变量的数量,local参数表示局部变量数组,nStack参数表示操作数栈的数量,stack参数表示操作数栈数组
   void visitFrame(int type, int nLocal, Object[] local, int nStack,
   Object[] stack);
   // 访问方法的一条指令,指令没有操作数
   void visitInsn(int opcode);
   // 访问方法的一条指令,指令操作数为单个整数
   void visitIntInsn(int opcode, int operand);
   // 访问方法的一条指令,指令操作数为局部变量索引
   void visitVarInsn(int opcode, int var);
   // 访问方法的一条指令,指令操作数为类型描述符
   void visitTypeInsn(int opcode, String desc);
   // 访问方法的一条指令,指令操作数为字段的信息。opc参数表示指令的操作码,owner参数表示字段所属的类名,name参数表示字段的名称,desc参数表示字段的描述符
   void visitFieldInsn(int opc, String owner, String name, String desc);
   // 访问方法的一条指令,指令操作数为方法的信息。opc参数表示指令的操作码,owner参数表示方法所属的类名,name参数表示方法的名称,desc参数表示方法的描述符
   void visitMethodInsn(int opc, String owner, String name, String desc);
   // 访问方法的一条动态方法调用指令。name参数表示方法的名称,desc参数表示方法的描述符,bsm参数表示引导方法(bootstrap method)的句柄,bsmArgs参数表示引导方法的参数。
   void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs);
   // 访问方法的一条跳转指令
   void visitJumpInsn(int opcode, Label label);
   // 访问方法的标签(Label),用于标记代码的位置
   void visitLabel(Label label);
   // 访问方法的一条指令,将常量加载到操作数栈上
   void visitLdcInsn(Object cst);
   // 访问方法的一条指令,对局部变量进行增量操作
   void visitIincInsn(int var, int increment);
   // 访问方法的一条表格跳转指令。min参数表示最小的键值,max参数表示最大的键值,dflt参数表示默认跳转目标的标签,labels参数表示每个键值对应的跳转目标的标签数组
   void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);
   // 访问方法的一条查找跳转指令。dflt参数表示默认跳转目标的标签,keys参数表示键值数组,labels参数表示每个键值对应的跳转目标的标签数组
   void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
   // 访问方法的一条多维数组创建指令。desc参数表示数组的元素类型的描述符,dims参数表示数组的维度
   void visitMultiANewArrayInsn(String desc, int dims);
   // 访问方法的一个try-catch块。start参数表示try块的起始标签,end参数表示try块的结束标签,handler参数表示catch块的处理程序标签,type参数表示捕获的异常类型的描述符
   void visitTryCatchBlock(Label start, Label end, Label handler, String type);
   // 访问方法的局部变量。name参数表示局部变量的名称,desc参数表示局部变量的描述符,signature参数表示局部变量的泛型签名(如果适用),start参数表示变量的作用域的起始标签,end参数表示变量的作用域的结束标签,index参数表示局部变量的索引
   void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index);
   // 访问方法的行号信息。line参数表示行号,start参数表示行号对应的代码位置的标签
   void visitLineNumber(int line, Label start);
   // 访问方法的最大栈大小和最大局部变量数量。maxStack参数表示最大栈大小,maxLocals参数表示最大局部变量数量
   void visitMaxs(int maxStack, int maxLocals);
   // 访问方法的结束,表示不再访问该方法的任何内容
   void visitEnd();
}

修改方法的步骤:先看原始方法编译后对应的字节码长什么样,再看你想要的修改结果编译后字节码长什么样,最后把“两者差异”翻译成 visit... 调用。也就是先对比字节码差异,再用 MethodVisitor 按差异去插入/修改指令。

修改字节码:

添加与删除Field:

实例:

package javaasm;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;

public class UpdateFieldClassVisitor extends ClassVisitor {
    // 删除字段的name
    private String deleteFieldName;
    // 添加字段的访问修饰符
    private int addFieldAcc;
    // 添加字段的name
    private String addFieldName;
    // 添加字段的描述符(类型)
    private String addFieldDesc;

    private Boolean flag = false;

    protected UpdateFieldClassVisitor(ClassVisitor cv, String deleteFieldName, int addFieldAcc, String addFieldName, String addFieldDesc) {
        super(Opcodes.ASM5, cv);
        this.deleteFieldName = deleteFieldName;
        this.addFieldAcc = addFieldAcc;
        this.addFieldName = addFieldName;
        this.addFieldDesc = addFieldDesc;
    }

    @Override
    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        // 删除名为deleteFieldName的字段
        if (name.equals(deleteFieldName)) {
            return null; 
            //不再向后传入,不写进新的class里面
        }
        if (name.equals(addFieldName)) flag = true;
        return super.visitField(access, name, descriptor, signature, value);
    }

    @Override
    public void visitEnd() {
        // 添加名为addFieldName的字段
        if (!flag) {
            FieldVisitor fieldVisitor = super.visitField(addFieldAcc, addFieldName, addFieldDesc, null, null); 
            //主动向后面的 visitor 发出一个新的字段事件,相当于手动构造一个字段,让后面的 ClassWriter 把它写进去。
            if (fieldVisitor != null) {
                fieldVisitor.visitEnd(); 
                //表示传入已经结束
            }
        }
        super.visitEnd();
    }
}

测试:

package javaasm;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) throws Exception{
        // -------------------添加与删除Field-------------------
        FileInputStream stream = new FileInputStream("target/classes/bytecode/bytecodeTest.class");
        // 加载字节码
        ClassReader reader = new ClassReader(stream);
        ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
        // 实例化修改Field Visitor
        UpdateFieldClassVisitor updateFieldClassVisitor = new UpdateFieldClassVisitor(writer, "sex", Opcodes.ACC_PRIVATE, "address", "Ljava/lang/String;");
        // 调用accept
        reader.accept(updateFieldClassVisitor, ClassReader.EXPAND_FRAMES);
        FileOutputStream fileOutputStream = new FileOutputStream("temp.class");
        byte[] updateByte = writer.toByteArray();
        fileOutputStream.write(updateByte);
        fileOutputStream.close();
        // 测试
        ClassReader classReader = new ClassReader(updateByte);
        MyClassVisitor myClassVisitor = new MyClassVisitor();
        classReader.accept(myClassVisitor, 0);
    }
}
添加与删除Method:

实例:

package javaasm;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class UpdateMethodClassVisitor extends ClassVisitor {
    private String deleteMethodName;
    private String deleteMethodDesc;
    private int addMethodAcc;
    private String addMethodName;
    private String addMethodDesc;
    private boolean flag = false;

    protected UpdateMethodClassVisitor(ClassVisitor cv, String deleteMethodName, String deleteMethodDesc, int addMethodAcc,
                                       String addMethodName, String addMethodDesc) {
        super(Opcodes.ASM5, cv);
        this.deleteMethodName = deleteMethodName;
        this.deleteMethodDesc = deleteMethodDesc;
        this.addMethodAcc = addMethodAcc;
        this.addMethodName = addMethodName;
        this.addMethodDesc = addMethodDesc;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        // 删除名为deleteMethodName且描述为deleteMethodDesc的方法
        // 因为有的方法可能name一致,但是参数不同
        if (name.equals(deleteMethodName) && descriptor.equals(deleteMethodDesc)) {
            return null;
        }
        if (name.equals(addMethodName) && descriptor.equals(addMethodDesc)) flag = true;
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }

    @Override
    public void visitEnd() {
        // 添加名为addMethodName且描述为addMethodDesc的方法
        if (!flag) {
            MethodVisitor methodVisitor = super.visitMethod(addMethodAcc, addMethodName, addMethodDesc, null, null);
            if (methodVisitor != null) {
                // 访问方法的字节码
                methodVisitor.visitCode();
                // 添加return指令
                methodVisitor.visitInsn(Opcodes.RETURN);
                // 设置方法的最大操作数栈深度和最大局部变量表大小,空方法设置00即可
                methodVisitor.visitMaxs(0, 0);
                // 结束访问
                methodVisitor.visitEnd();
            }
        }
        super.visitEnd();
    }
}

测试:

package javaasm;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) throws Exception{
        // -------------------添加与删除Method-------------------
        FileInputStream stream = new FileInputStream("target/classes/bytecode/bytecodeTest.class");
        // 加载字节码
        ClassReader reader = new ClassReader(stream);
        ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
        // 实例化修改Method Visitor
        UpdateMethodClassVisitor updateMethodClassVisitor = new UpdateMethodClassVisitor(writer, "sayHello", "()V", Opcodes.ACC_PUBLIC, "newMethod", "()V");
        reader.accept(updateMethodClassVisitor, ClassReader.EXPAND_FRAMES);
        // 写入新class中
        FileOutputStream fileOutputStream = new FileOutputStream("temp.class");
        byte[] updateByte = writer.toByteArray();
        fileOutputStream.write(updateByte);
        fileOutputStream.close();
        // 测试
        ClassReader classReader = new ClassReader(updateByte);
        MyClassVisitor myClassVisitor = new MyClassVisitor();
        classReader.accept(myClassVisitor, 0);
    }
}
修改方法指令:

实例:

进行实际修改的方法适配器:
package javaasm;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class ModMethodAdapter extends MethodVisitor {

    public ModMethodAdapter(MethodVisitor methodVisitor) {
        super(Opcodes.ASM5, methodVisitor);
    }

    @Override
    public void visitCode() {
        // 在方法前面添加输出
        // 从java/lang/System类中获取名为out的静态字段,该字段的类型为java/io/PrintStream
        // GETSTATIC指令将该字段的值压入操作数栈上
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // LDC指令用于将常量加载到栈上,out是字段名,java/lang/System是字段定义所在的类,Ljava/io/PrintStream是字段描述符,表示参数/字段1          类型
        mv.visitLdcInsn("Hello, World!");
        // 调用java/io/PrintStream类的println方法,它接受一个java/lang/String类型的参数,并且没有返回值
        // INVOKEVIRTUAL指令用于调用实例方法
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        //println是方法名,带括号的那个参数是方法描述符,格式:( 参数1描述符 参数2描述符 ... ) 返回值描述符
        super.visitCode();
    }
} 

将字节码传给方法适配器的访问者类:
package javaasm;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class ModMethodVisitor extends ClassVisitor {
    protected ModMethodVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM5, classVisitor);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
        return new ModMethodAdapter(methodVisitor);
    }
} 

实际测试代码:
package javaasm;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) throws Exception{
        // -------------------修改Method:向方法开头加入输出-------------------
        FileInputStream stream = new FileInputStream("target/classes/bytecode/bytecodeTest.class");
        // 加载字节码
        ClassReader reader = new ClassReader(stream);
        ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
        // 实例化修改Method Visitor
        ModMethodVisitor modMethodVisitor = new ModMethodVisitor(writer);
        reader.accept(modMethodVisitor, ClassReader.EXPAND_FRAMES);
        // 写入新class中
        FileOutputStream fileOutputStream = new FileOutputStream("temp.class");
        byte[] updateByte = writer.toByteArray();
        fileOutputStream.write(updateByte);
        fileOutputStream.close();
        // 测试
        ClassReader classReader = new ClassReader(updateByte);
        MyClassVisitor myClassVisitor = new MyClassVisitor();
        classReader.accept(myClassVisitor, 0);
    }
}

指令集:

说明:

分类 指令的功能类别
字节码指令 JVM 规范中的指令名(如 GETSTATIC
ASM 方法 你在 MethodVisitor 中调用的方法(如 visitFieldInsn
栈变化 执行后操作数栈的变化( 表示执行后)
示例 典型使用场景

字段操作 (visitFieldInsn):

分类 ASM 方法 关键参数 常用 Opcodes Java 代码对应
字段访问 visitFieldInsn(int opcode, String owner, String name, String desc) owner: 类内部名
name: 字段名
desc: 字段描述符
GETSTATIC
PUTSTATIC
GETFIELD
PUTFIELD
System.out
System.setOut()
obj.name
obj.name = "v"

方法调用 (visitMethodInsn / visitInvokeDynamicInsn):

分类 ASM 方法 关键参数 常用 Opcodes Java 代码对应
方法调用 visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) owner: 类内部名
name: 方法名
desc: 方法描述符
isInterface: 接口标志
INVOKEVIRTUAL
INVOKESPECIAL
INVOKESTATIC
INVOKEINTERFACE
obj.toString()
new Object() / super.m()
Math.abs()
list.add()
动态调用 visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) bsm: 引导方法句柄
bsmArgs: 引导参数
(无 Opcode 参数) Lambda 表达式
(Runnable)->{}

变量与常量 (visitVarInsn / visitLdcInsn / visitInsn):

分类 ASM 方法 关键参数 常用 Opcodes Java 代码对应
局部变量 visitVarInsn(int opcode, int var) var: 局部变量索引 ILOAD, ISTORE
ALOAD, ASTORE
LLOAD, LSTORE
int x
Object obj
long l
常量加载 visitLdcInsn(Object value) value: 常量值 (String/Int/Class) (无 Opcode 参数) "Hello"
123
String.class
简单指令 visitInsn(int opcode) 无额外参数 ICONST_0ICONST_5
IADD, ISUB
POP, DUP
RETURN, ARETURN
05
+, -
弹出栈顶,复制栈顶
return

类型与对象 (visitTypeInsn):

分类 ASM 方法 关键参数 常用 Opcodes Java 代码对应
类型操作 visitTypeInsn(int opcode, String type) type: 类内部名 (如 java/lang/String) NEW
ANEWARRAY
CHECKCAST
INSTANCEOF
new String()
new String[10]
(String)obj
obj instanceof String

控制流 (visitJumpInsn / visitLabel):

分类 ASM 方法 关键参数 常用 Opcodes Java 代码对应
跳转 visitJumpInsn(int opcode, Label label) label: 跳转目标标记 GOTO
IFEQ, IFNE
IF_ICMPEQ
goto L1
if (x == 0)
if (a == b)
多分支 visitTableSwitchInsn
visitLookupSwitchInsn
dflt: 默认标签
labels: 分支标签
(无 Opcode 参数) switch(x)
标记 visitLabel(Label label) label: 标记对象 (无 Opcode 参数) 跳转目标位置

方法生命周期 (必须调用):

方法 调用时机 参数说明 注意事项
visitCode() 方法体开始 插入代码的入口,必须调用 super.visitCode()
visitMaxs(int maxStack, int maxLocals) 方法体结束 栈深度,局部变量槽数 若使用 COMPUTE_FRAMES 可传 0, 0
visitEnd() 方法完全结束 必须调用,否则类文件损坏

再附一个方法字段名所属表:

System 类 (java/lang/System):

类型 名称 描述符 完整 ASM 参数 用途
字段 out Ljava/io/PrintStream; "java/lang/System", "out", "Ljava/io/PrintStream;" 标准输出
字段 in Ljava/io/InputStream; "java/lang/System", "in", "Ljava/io/InputStream;" 标准输入
字段 err Ljava/io/PrintStream; "java/lang/System", "err", "Ljava/io/PrintStream;" 错误输出
方法 currentTimeMillis ()J "java/lang/System", "currentTimeMillis", "()J", false 获取时间戳
方法 nanoTime ()J "java/lang/System", "nanoTime", "()J", false 获取纳秒时间
方法 arraycopy (Ljava/lang/Object;ILjava/lang/Object;II)V "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", false 数组复制
方法 getProperty (Ljava/lang/String;)Ljava/lang/String; "java/lang/System", "getProperty", "(Ljava/lang/String;)Ljava/lang/String;", false 获取系统属性
方法 exit (I)V "java/lang/System", "exit", "(I)V", false 退出 JVM

PrintStream 类 (java/io/PrintStream):

类型 名称 描述符 完整 ASM 参数 用途
方法 println (Ljava/lang/String;)V "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false 打印字符串
方法 println (I)V "java/io/PrintStream", "println", "(I)V", false 打印 int
方法 println (J)V "java/io/PrintStream", "println", "(J)V", false 打印 long
方法 println (Z)V "java/io/PrintStream", "println", "(Z)V", false 打印 boolean
方法 println (Ljava/lang/Object;)V "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false 打印对象
方法 print (Ljava/lang/String;)V "java/io/PrintStream", "print", "(Ljava/lang/String;)V", false 打印字符串 (不换行)
方法 printf (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; "java/io/PrintStream", "printf", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;", false 格式化打印
方法 flush ()V "java/io/PrintStream", "flush", "()V", false 刷新缓冲区
方法 close ()V "java/io/PrintStream", "close", "()V", false 关闭流String 类(java/lang/String):

String 类 (java/lang/String):

方法 length ()I "java/lang/String", "length", "()I", false 获取长度
方法 charAt (I)C "java/lang/String", "charAt", "(I)C", false 获取字符
方法 substring (I)Ljava/lang/String; "java/lang/String", "substring", "(I)Ljava/lang/String;", false 截取字符串
方法 substring (II)Ljava/lang/String; "java/lang/String", "substring", "(II)Ljava/lang/String;", false 截取字符串 (范围)
方法 equals (Ljava/lang/Object;)Z "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false 比较相等
方法 equalsIgnoreCase (Ljava/lang/String;)Z "java/lang/String", "equalsIgnoreCase", "(Ljava/lang/String;)Z", false 忽略大小写比较
方法 startsWith (Ljava/lang/String;)Z "java/lang/String", "startsWith", "(Ljava/lang/String;)Z", false 判断前缀
方法 endsWith (Ljava/lang/String;)Z "java/lang/String", "endsWith", "(Ljava/lang/String;)Z", false 判断后缀
方法 contains (Ljava/lang/CharSequence;)Z "java/lang/String", "contains", "(Ljava/lang/CharSequence;)Z", false 判断包含
方法 indexOf (I)I "java/lang/String", "indexOf", "(I)I", false 查找字符索引
方法 indexOf (Ljava/lang/String;)I "java/lang/String", "indexOf", "(Ljava/lang/String;)I", false 查找字符串索引
方法 replace (CC)Ljava/lang/String; "java/lang/String", "replace", "(CC)Ljava/lang/String;", false 替换字符
方法 replace (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; "java/lang/String", "replace", "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;", false 替换字符串
方法 split (Ljava/lang/String;)[Ljava/lang/String; "java/lang/String", "split", "(Ljava/lang/String;)[Ljava/lang/String;", false 分割字符串
方法 trim ()Ljava/lang/String; "java/lang/String", "trim", "()Ljava/lang/String;", false 去除空白
方法 toLowerCase ()Ljava/lang/String; "java/lang/String", "toLowerCase", "()Ljava/lang/String;", false 转小写
方法 toUpperCase ()Ljava/lang/String; "java/lang/String", "toUpperCase", "()Ljava/lang/String;", false 转大写
方法 valueOf (I)Ljava/lang/String; "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false int 转 String (静态)
方法 valueOf (Ljava/lang/Object;)Ljava/lang/String; "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false Object 转 String (静态)

Object 类 (java/lang/Object):

类型 名称 描述符 完整 ASM 参数 用途
方法 <init> ()V "java/lang/Object", "<init>", "()V", false 构造方法
方法 toString ()Ljava/lang/String; "java/lang/Object", "toString", "()Ljava/lang/String;", false 转字符串
方法 equals (Ljava/lang/Object;)Z "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false 比较相等
方法 hashCode ()I "java/lang/Object", "hashCode", "()I", false 获取哈希码
方法 getClass ()Ljava/lang/Class; "java/lang/Object", "getClass", "()Ljava/lang/Class;", false 获取 Class 对象
方法 notify ()V "java/lang/Object", "notify", "()V", false 唤醒线程
方法 notifyAll ()V "java/lang/Object", "notifyAll", "()V", false 唤醒所有线程
方法 wait ()V "java/lang/Object", "wait", "()V", false 等待
方法 wait (J)V "java/lang/Object", "wait", "(J)V", false 等待 (超时)

Math 类 (java/lang/Math):

类型 名称 描述符 完整 ASM 参数 用途
方法 abs (I)I "java/lang/Math", "abs", "(I)I", false 绝对值 (int)
方法 abs (J)J "java/lang/Math", "abs", "(J)J", false 绝对值 (long)
方法 abs (F)F "java/lang/Math", "abs", "(F)F", false 绝对值 (float)
方法 abs (D)D "java/lang/Math", "abs", "(D)D", false 绝对值 (double)
方法 max (II)I "java/lang/Math", "max", "(II)I", false 最大值 (int)
方法 max (JJ)J "java/lang/Math", "max", "(JJ)J", false 最大值 (long)
方法 min (II)I "java/lang/Math", "min", "(II)I", false 最小值 (int)
方法 min (JJ)J "java/lang/Math", "min", "(JJ)J", false 最小值 (long)
方法 random ()D "java/lang/Math", "random", "()D", false 随机数
方法 sqrt (D)D "java/lang/Math", "sqrt", "(D)D", false 平方根
方法 pow (DD)D "java/lang/Math", "pow", "(DD)D", false 幂运算
方法 ceil (D)D "java/lang/Math", "ceil", "(D)D", false 向上取整
方法 floor (D)D "java/lang/Math", "floor", "(D)D", false 向下取整
方法 round (F)I "java/lang/Math", "round", "(F)I", false 四舍五入 (float)
方法 round (D)J "java/lang/Math", "round", "(D)J", false 四舍五入 (double)

Integer 类 (java/lang/Integer):

类型 名称 描述符 完整 ASM 参数 用途
字段 MAX_VALUE I "java/lang/Integer", "MAX_VALUE", "I" 最大 int 值
字段 MIN_VALUE I "java/lang/Integer", "MIN_VALUE", "I" 最小 int 值
方法 valueOf (I)Ljava/lang/Integer; "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false int 转 Integer
方法 intValue ()I "java/lang/Integer", "intValue", "()I", false Integer 转 int
方法 parseInt (Ljava/lang/String;)I "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false String 转 int
方法 toString (I)Ljava/lang/String; "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false int 转 String

Long 类 (java/lang/Long):

类型 名称 描述符 完整 ASM 参数 用途
字段 MAX_VALUE J "java/lang/Long", "MAX_VALUE", "J" 最大 long 值
字段 MIN_VALUE J "java/lang/Long", "MIN_VALUE", "J" 最小 long 值
方法 valueOf (J)Ljava/lang/Long; "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false long 转 Long
方法 longValue ()J "java/lang/Long", "longValue", "()J", false Long 转 long
方法 parseLong (Ljava/lang/String;)J "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false String 转 long
方法 toString (J)Ljava/lang/String; "java/lang/Long", "toString", "(J)Ljava/lang/String;", false long 转 String

Boolean 类 (java/lang/Boolean)

类型 名称 描述符 完整 ASM 参数 用途
字段 TRUE Ljava/lang/Boolean; "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;" Boolean.TRUE
字段 FALSE Ljava/lang/Boolean; "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;" Boolean.FALSE
方法 valueOf (Z)Ljava/lang/Boolean; "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false boolean 转 Boolean
方法 booleanValue ()Z "java/lang/Boolean", "booleanValue", "()Z", false Boolean 转 boolean

ArrayList (java/util/ArrayList)

类型 名称 描述符 完整 ASM 参数 用途
方法 <init> ()V "java/util/ArrayList", "<init>", "()V", false 构造方法
方法 add (Ljava/lang/Object;)Z "java/util/ArrayList", "add", "(Ljava/lang/Object;)Z", false 添加元素
方法 get (I)Ljava/lang/Object; "java/util/ArrayList", "get", "(I)Ljava/lang/Object;", false 获取元素
方法 size ()I "java/util/ArrayList", "size", "()I", false 获取大小
方法 remove (I)Ljava/lang/Object; "java/util/ArrayList", "remove", "(I)Ljava/lang/Object;", false 删除元素
方法 clear ()V "java/util/ArrayList", "clear", "()V", false 清空列表

List 接口 (java/util/List)

类型 名称 描述符 完整 ASM 参数 用途
方法 add (Ljava/lang/Object;)Z "java/util/List", "add", "(Ljava/lang/Object;)Z", true 添加元素
方法 get (I)Ljava/lang/Object; "java/util/List", "get", "(I)Ljava/lang/Object;", true 获取元素
方法 size ()I "java/util/List", "size", "()I", true 获取大小
方法 iterator ()Ljava/util/Iterator; "java/util/List", "iterator", "()Ljava/util/Iterator;", true 获取迭代器

Map 接口 (java/util/Map)

类型 名称 描述符 完整 ASM 参数 用途
方法 put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true 放入键值对
方法 get (Ljava/lang/Object;)Ljava/lang/Object; "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true 获取值
方法 remove (Ljava/lang/Object;)Ljava/lang/Object; "java/util/Map", "remove", "(Ljava/lang/Object;)Ljava/lang/Object;", true 删除键值对
方法 size ()I "java/util/Map", "size", "()I", true 获取大小
方法 keySet ()Ljava/util/Set; "java/util/Map", "keySet", "()Ljava/util/Set;", true 获取键集合
方法 values ()Ljava/util/Collection; "java/util/Map", "values", "()Ljava/util/Collection;", true 获取值集合
方法 containsKey (Ljava/lang/Object;)Z "java/util/Map", "containsKey", "(Ljava/lang/Object;)Z", true 判断包含键

HashMap (java/util/HashMap):

类型 名称 描述符 完整 ASM 参数 用途
方法 <init> ()V "java/util/HashMap", "<init>", "()V", false 构造方法

数组操作 (java/lang/reflect/Array):

类型 名称 描述符 完整 ASM 参数 用途
方法 getLength (Ljava/lang/Object;)I "java/lang/reflect/Array", "getLength", "(Ljava/lang/Object;)I", false 获取数组长度
方法 get (Ljava/lang/Object;I)Ljava/lang/Object; "java/lang/reflect/Array", "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", false 获取数组元素
方法 set (Ljava/lang/Object;ILjava/lang/Object;)V "java/lang/reflect/Array", "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", false 设置数组元素
方法 newInstance (Ljava/lang/Class;I)Ljava/lang/Object; "java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;", false 创建数组

Thread 类 (java/lang/Thread):

类型 名称 描述符 完整 ASM 参数 用途
方法 currentThread ()Ljava/lang/Thread; "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false 获取当前线程
方法 sleep (J)V "java/lang/Thread", "sleep", "(J)V", false 线程休眠
方法 getName ()Ljava/lang/String; "java/lang/Thread", "getName", "()Ljava/lang/String;", false 获取线程名
方法 start ()V "java/lang/Thread", "start", "()V", false 启动线程
方法 run ()V "java/lang/Thread", "run", "()V", false 运行线程
方法 interrupt ()V "java/lang/Thread", "interrupt", "()V", false 中断线程
方法 isInterrupted ()Z "java/lang/Thread", "isInterrupted", "()Z", false 判断是否中断

Class 类(java/lang/Class):

类型 名称 描述符 完整 ASM 参数 用途
方法 getName ()Ljava/lang/String; "java/lang/Class", "getName", "()Ljava/lang/String;", false 获取类名
方法 getSimpleName ()Ljava/lang/String; "java/lang/Class", "getSimpleName", "()Ljava/lang/String;", false 获取简单类名
方法 getSuperclass ()Ljava/lang/Class; "java/lang/Class", "getSuperclass", "()Ljava/lang/Class;", false 获取父类
方法 isArray ()Z "java/lang/Class", "isArray", "()Z", false 判断是否数组
方法 isInterface ()Z "java/lang/Class", "isInterface", "()Z", false 判断是否接口
方法 forName (Ljava/lang/String;)Ljava/lang/Class; "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false 加载类 (静态)

StringBuilder 类 (java/lang/StringBuilder):

类型 名称 描述符 完整 ASM 参数 用途
方法 <init> ()V "java/lang/StringBuilder", "<init>", "()V", false 构造方法
方法 <init> (Ljava/lang/String;)V "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false 构造方法 (带初始值)
方法 append (Ljava/lang/String;)Ljava/lang/StringBuilder; "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false 追加字符串
方法 append (I)Ljava/lang/StringBuilder; "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false 追加 int
方法 append (J)Ljava/lang/StringBuilder; "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false 追加 long
方法 append (Z)Ljava/lang/StringBuilder; "java/lang/StringBuilder", "append", "(Z)Ljava/lang/StringBuilder;", false 追加 boolean
方法 append (Ljava/lang/Object;)Ljava/lang/StringBuilder; "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false 追加对象
方法 toString ()Ljava/lang/String; "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false 转 String

描述符:

基础类型

Java 类型 描述符
byte B
char C
double D
float F
int I
long J
short S
boolean Z
void V

引用类型

Java 类型 描述符
String Ljava/lang/String;
Object Ljava/lang/Object;
Class Ljava/lang/Class;
Thread Ljava/lang/Thread;

数组类型

Java 类型 描述符
int[] [I
long[] [J
String[] [Ljava/lang/String;
Object[] [Ljava/lang/Object;
int[][] [[I
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇