You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过ASM Visitor识别被调用静态方法所属的类类型?

在ASM Visitor中识别被调用的静态方法所属类

嘿,这个问题我太熟了!要在ASM里追踪类A对类B的静态方法调用,核心就是在**方法访问器(MethodVisitor)**里拦截静态方法调用的字节码指令,然后提取目标类的信息。下面给你一步步拆解怎么做:

核心原理

ASM是基于字节码指令分析的,静态方法调用对应的字节码指令是invokestatic。这个指令的操作数包含了三个关键信息:

  • owner:被调用静态方法所属类的内部名称(用/代替.,比如com/example/B)
  • name:方法名(比如b)
  • descriptor:方法的描述符(比如()V表示无参无返回值)

我们只需要拦截这个指令,就能拿到owner,也就是类B的信息,进而判断当前访问的类A是否引用了B。

具体实现步骤

1. 自定义ClassVisitor追踪当前类

首先需要自定义一个ClassVisitor,用来记录当前正在访问的类(也就是类A)的名称,同时在进入类的方法时,返回一个自定义的MethodVisitor来拦截方法调用指令。

2. 自定义MethodVisitor拦截invokestatic指令

在自定义的MethodVisitor中,重写visitMethodInsn方法——这个方法会在遇到任何方法调用指令时被触发。我们在这里判断指令类型是否为INVOKESTATIC,然后解析owner参数即可。

完整代码示例

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

// 自定义ClassVisitor,负责追踪当前访问的类,并创建方法访问器
public class DependencyTrackingClassVisitor extends ClassVisitor {
    private String currentClassName;

    public DependencyTrackingClassVisitor(ClassVisitor cv) {
        super(Opcodes.ASM9, cv); // 使用ASM9版本,根据你的依赖调整版本号
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // 记录当前类的内部名称(比如com/example/A)
        this.currentClassName = name;
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
        // 返回自定义的MethodVisitor来拦截方法调用指令
        return new DependencyTrackingMethodVisitor(mv, currentClassName);
    }

    // 自定义MethodVisitor,负责拦截静态方法调用
    private static class DependencyTrackingMethodVisitor extends MethodVisitor {
        private String currentClassName;

        public DependencyTrackingMethodVisitor(MethodVisitor mv, String currentClassName) {
            super(Opcodes.ASM9, mv);
            this.currentClassName = currentClassName;
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            // 只处理静态方法调用指令
            if (opcode == Opcodes.INVOKESTATIC) {
                // 将内部名称转换为常规类名(比如com/example/B -> com.example.B)
                String callingClass = currentClassName.replace('/', '.');
                String calledClass = owner.replace('/', '.');
                
                System.out.printf("检测到:类 %s 的方法调用了静态类 %s 的方法 %s%n", 
                        callingClass, calledClass, name);
                
                // 如果你需要精准判断是否是类B,可以直接匹配
                if ("com.example.B".equals(calledClass)) {
                    System.out.println("✅ 确认类A使用了类B!");
                }
            }
            // 别忘了调用父类方法,保证后续处理正常执行
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }
    }
}

3. 使用Visitor分析类A的字节码

接下来用ClassReader读取类A的字节码,并传入我们自定义的Visitor:

import org.objectweb.asm.ClassReader;
import java.io.IOException;
import java.io.InputStream;

public class ASMDependencyTracker {
    public static void main(String[] args) throws IOException {
        // 读取类A的字节码(这里假设类A的全限定名是com.example.A)
        try (InputStream classInputStream = ASMDependencyTracker.class.getClassLoader()
                .getResourceAsStream("com/example/A.class")) {
            
            ClassReader classReader = new ClassReader(classInputStream);
            // 创建自定义Visitor,这里传入null表示不需要后续的ClassVisitor处理
            DependencyTrackingClassVisitor visitor = new DependencyTrackingClassVisitor(null);
            // 开始分析字节码,EXPAND_FRAMES表示展开栈帧(可选,但推荐)
            classReader.accept(visitor, ClassReader.EXPAND_FRAMES);
        }
    }
}

关键注意事项

  • ASM版本:代码中用的是ASM9,要确保你的项目依赖的ASM jar版本和这里一致(比如在Maven中引入org.ow2.asm:asm:9.5)。
  • 类名格式:ASM中的owner参数是类的内部名称(用/分隔包名),需要转换为我们熟悉的.分隔格式才能直观识别。
  • 扩展性:如果需要批量分析多个类,可以遍历classpath或指定目录下的所有.class文件,逐个用ClassReader处理。
  • 其他指令:如果还要追踪其他类型的方法调用(比如实例方法、接口方法),可以对应处理INVOKEVIRTUAL、INVOKEINTERFACE等指令,但这里只需要处理INVOKESTATIC即可。

内容的提问来源于stack exchange,提问作者Mcm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:34:08