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

如何基于ASM查找指定类所有方法的调用者并输出调用关系至文件?

用ASM追踪ExceptionFactory调用链路并生成结果文件

核心逻辑

直接用ASM遍历服务层类的字节码,捕获所有调用ExceptionFactory方法的指令,记录调用方(服务层方法)和被调用方(ExceptionFactory的方法)的对应关系,最后输出到文件。把这套逻辑做成JUnit测试用例,跑测试时自动执行扫描并生成结果。

具体实现步骤

1. 引入ASM依赖

如果用Maven,直接在pom.xml里加测试依赖:

<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.5</version>
    <scope>test</scope>
</dependency>

2. 编写ASM追踪器

自定义ClassVisitor和MethodVisitor,用来识别并记录调用关系:

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;

public class ExceptionFactoryCallTracker extends ClassVisitor {
    // 替换成你的ExceptionFactory全类名,注意用/代替.
    private static final String TARGET_CLASS = "com/yourproject/exception/ExceptionFactory";
    private final PrintWriter writer;
    private String currentClassName;
    private final Set<String> recordedCalls = new HashSet<>(); // 避免重复记录同一调用

    public ExceptionFactoryCallTracker(ClassVisitor cv, PrintWriter writer) {
        super(Opcodes.ASM9, cv);
        this.writer = writer;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // 把ASM的类名格式(用/分隔)转成标准Java类名
        this.currentClassName = name.replace('/', '.');
        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);
        // 给每个方法绑定追踪器,记录当前方法的信息
        return new MethodCallTracker(mv, currentClassName, name);
    }

    private class MethodCallTracker extends MethodVisitor {
        private final String callerClass;
        private final String callerMethod;

        public MethodCallTracker(MethodVisitor mv, String callerClass, String callerMethod) {
            super(Opcodes.ASM9, mv);
            this.callerClass = callerClass;
            this.callerMethod = callerMethod;
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            // 匹配调用ExceptionFactory的指令
            if (TARGET_CLASS.equals(owner)) {
                String targetMethod = owner.replace('/', '.') + "." + name;
                String callEntry = callerClass + "." + callerMethod + " -> " + targetMethod;
                // 去重后写入文件
                if (recordedCalls.add(callEntry)) {
                    writer.println(callEntry);
                }
            }
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }
    }
}

3. 编写类扫描工具

用来遍历指定目录下的所有类文件:

import org.objectweb.asm.ClassReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintWriter;

public class ClassScanner {
    public static void scanClasses(File classDir, PrintWriter writer) throws Exception {
        if (!classDir.isDirectory()) {
            return;
        }
        File[] files = classDir.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                // 递归扫描子目录
                scanClasses(file, writer);
            } else if (file.getName().endsWith(".class")) {
                // 读取类文件并交给ASM处理
                try (FileInputStream fis = new FileInputStream(file)) {
                    ClassReader cr = new ClassReader(fis);
                    cr.accept(new ExceptionFactoryCallTracker(null, writer), ClassReader.SKIP_DEBUG);
                }
            }
        }
    }
}

4. 集成到JUnit测试用例

这样跑测试时自动执行扫描,生成结果文件:

import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.PrintWriter;

public class ExceptionCallTrackingTest {
    @Test
    public void trackExceptionFactoryCalls() throws Exception {
        // 替换成你的服务层类编译后的目录,比如target/classes/com/yourproject/service
        File serviceClassDir = new File("target/classes/com/yourproject/service");
        // 输出结果的文件路径
        try (PrintWriter writer = new PrintWriter("exception_service_calls.txt")) {
            ClassScanner.scanClasses(serviceClassDir, writer);
        }
    }
}

可选优化

  • 要是只想追踪ExceptionFactory里生成特定异常的方法,可以在visitMethodInsn里加方法名或描述符的判断,比如只匹配createUserNotFoundException这类方法
  • 如果需要完整调用链路(比如上层控制器调用服务层,服务层调用ExceptionFactory),可以扩展扫描范围到控制器目录,然后递归追踪调用关系,但要注意处理循环引用
  • 可以把输出结果按类分组,或者加上方法参数信息,方便更精准定位

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:30:55