跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

基于 ASM+Maven 插件实现 Java 方法调用链分析

介绍如何使用 ASM 字节码解析框架构建 Maven 插件,实现无侵入式的 Java 方法调用链分析。通过解析编译后的 .class 文件,支持包过滤、循环检测及树形可视化输出。适用于老旧代码重构、依赖梳理、死代码识别及故障排查等场景。教程包含环境准备、核心原理、插件开发步骤及测试使用方法。

颠三倒四发布于 2026/3/25更新于 2026/6/124 浏览
基于 ASM+Maven 插件实现 Java 方法调用链分析

基于 ASM+Maven 插件实现 Java 方法调用链分析

本文基于 ASM 字节码解析开发的 Maven 调用链分析插件,实现无侵入式的类方法调用链分析,无需运行业务代码,直接解析编译后的 .class 文件,快速定位指定类的方法调用关系。结合其「包过滤、循环调用检测、树形可视化、字节码级无侵入」等特性,适用于以下场景:

  • 老旧代码重构:解析调用链、标记循环调用,明确修改边界
  • 大型项目依赖梳理:梳理模块间方法依赖,定位关键调用节点
  • 死代码识别:检测无调用的方法,辅助代码瘦身
  • 故障排查:快速追溯异常方法的上下游调用链
  • 接口变更评估:识别方法的调用方,评估变更影响范围
  • 代码合规审计:检查核心类是否调用违规方法 / 第三方包
  • 性能瓶颈定位:识别调用层级过深(如超过 5 层)、循环调用等性能问题

一、前置准备

1.1 开发环境

  • JDK 21+(ASM 对 JDK 8 兼容性最佳,也是企业级项目主流版本)
  • Maven 3.8+(插件开发基础依赖)
  • IDE(IntelliJ IDEA/Eclipse,推荐 IDEA 自带 Maven 插件调试功能)

1.2 ASM 核心认知

ASM 是轻量级字节码操作框架,核心通过「读取 - 遍历 - 解析」三步识别方法调用:

  • ClassReader:读取 .class 文件的字节码流;
  • ClassVisitor:遍历类结构(类名、方法、字段);
  • MethodVisitor:遍历方法内的指令,识别 invokevirtual/invokespecial/invokestatic/invokeinterface 等方法调用指令。

二、插件核心原理

插件整体执行逻辑:

  1. 遍历指定目录下的所有 .class 文件;
  2. 对每个 .class,通过 ASM 解析类内所有方法及方法调用指令;
  3. 构建「调用方 → 被调用方」的映射关系,存储调用链;
  4. 对调用链做过滤、循环检测、可视化输出。

三、Maven 插件开发

3.1 初始化 Maven 插件项目

方式一:用 Archetype 创建插件项目
  • 名称:填写项目名(如示例中的 call-chain-analyzer-maven-plugin)
  • JDK:选择项目使用的 JDK 版本(如示例中的 Oracle OpenJDK 21)
  • Archetype:选择 org.apache.maven.archetypes:maven-archetype-plugin(Maven 官方提供的插件项目模板)

点击右下角的'创建'按钮,Maven 会自动完成插件项目初始化,项目结构如下。

方式二:创建普通 Java 项目
  • 名称:填写项目名(如示例中的 call-chain-analyzer-maven-plugin)
  • JDK:选择项目使用的 JDK 版本(如示例中的 Oracle OpenJDK 21)
  • 构建系统:勾选'Maven'
  1. 执行 mvn clean compile 验证依赖是否正常(无报错即成功)。

在 pom.xml 中添加 Maven 插件的核心配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- 1. 项目基本信息 -->
    <groupId>com.example</groupId>
    <artifactId>call-chain-analyzer-maven-plugin</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>maven-plugin</packaging>
    <!-- 关键:打包类型为 maven-plugin -->
    <!-- 2. 依赖:Maven 插件核心 API -->
    <dependencies>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-plugin-api</artifactId>
            <version>3.9.11</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-core</artifactId>
            <version>3.9.11</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.plugin-tools</groupId>
            <artifactId>maven-plugin-annotations</artifactId>
            <version>3.15.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <!-- 3. 插件配置:编译注解 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.15.1</version>
                <executions>
                    <execution>
                        <id>default-descriptor</id>
                        <phase>process-classes</phase>
                    </execution>
                </executions>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

<packaging>maven-plugin</packaging> 用于声明项目为 Maven 插件,是插件项目的核心标识,缺失则无法被 Maven 识别

打开 IDEA → 选择'新建项目',在左侧菜单中点击'Java',填写项目基本信息,生成一个空的 Java 项目。

3.2 定义数据模型(MethodCallInfo)

创建 pojo/MethodCallInfo.java,用于存储单次调用'调用方'和'被调用方'的信息,是整个插件的数据载体:

/**
 * 方法调用信息模型:存储类方法单次调用的「调用方 - 被调用方」关系
 */
@Data
@AllArgsConstructor
public class MethodCallInfo {
    // 调用方类全限定名(如 com.example.demo.UserService)
    private String callerClass;
    // 调用方方法名(如 getUser)
    private String callerMethod;
    // 调用方方法描述符
    private String callerMethodDesc;
    // 被调用方类全限定名
    private String calleeClass;
    // 被调用方方法名
    private String calleeMethod;
    // 被调用方方法描述符
    private String calleeMethodDesc;

    // 格式化输出调用方方法
    public String getCallerMethodFull() {
        return callerClass + "#" + callerMethod + callerMethodDesc;
    }

    // 格式化输出被调用方方法
    public String getCalleeMethodFull() {
        return calleeClass + "#" + calleeMethod + calleeMethodDesc;
    }
}

添加构建配置,显式声明 Lombok 为注解处理器:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>${maven-compiler-plugin.version}</version>
    <configuration>
        <source>21</source>
        <target>21</target>
        <!-- 显式声明 Lombok 为注解处理器 -->
        <annotationProcessorPaths>
            <path>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.38</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

在 pom.xml 中添加 Lombok 依赖:

<!-- Lombok 依赖 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.38</version>
    <scope>provided</scope>
</dependency>

3.3 实现 ASM 解析逻辑

类级别访问器(ClassCallVisitor)

创建 ClassCallVisitor 类,继承 ASM 的 ClassVisitor,负责类层面的遍历与过滤,为符合条件的方法创建方法级访问器:

/**
 * 类级别的 ASM 解析器:遍历类结构,处理每个方法
 */
public class ClassCallVisitor extends ClassVisitor {
    private final Set<MethodCallInfo> methodCallSet;
    private String currentClass;
    private final List<String> scanPackages;
    private final List<String> excludeScanPackages;

    public ClassCallVisitor(int api, Set<MethodCallInfo> methodCallSet, List<String> scanPackages, List<String> excludeScanPackages) {
        super(api);
        this.methodCallSet = methodCallSet;
        this.scanPackages = scanPackages;
        this.excludeScanPackages = excludeScanPackages;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        // 将 ASM 格式类名(com/example/OrderService)转为全限定名(com.example.OrderService)
        this.currentClass = name.replace("/", ".");
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        // 跳过排除的包
        if (excludeScanPackages.stream().anyMatch(ignorePkg -> currentClass.startsWith(ignorePkg.trim() + "."))) {
            return super.visitMethod(access, name, descriptor, signature, exceptions);
        }
        MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
        // 跳过抽象方法、native 方法(无字节码,无法解析调用)
        if ((access & Opcodes.ACC_ABSTRACT) != 0 || (access & Opcodes.ACC_NATIVE) != 0) {
            return methodVisitor;
        }
        // 解析方法内的调用指令
        return new MethodCallVisitor(Opcodes.ASM9, methodVisitor, currentClass, name, context);
    }
}
方法级别访问器(MethodCallVisitor)

继承 ASM 的 MethodVisitor,负责方法内调用指令的解析与过滤,最终仅收集满足以下所有条件的方法调用关系:

  1. 调用方 / 被调用方均不属于'忽略包';
  2. 调用方不是构造方法 / 静态初始化方法;
  3. 被调用方不是构造方法 / 静态初始化方法;
  4. 若配置了'扫描包',被调用方必须属于'扫描包'范围(未配置则无此限制)。
/**
 * 方法级别的 ASM 解析器:识别方法内的调用指令,构建调用链
 */
public class MethodCallVisitor extends MethodVisitor {
    // 调用方类名(全限定名)
    private final String callerClass;
    // 调用方方法名
    private final String callerMethod;
    // 全局上下文
    private final CallChainContext context;

    public MethodCallVisitor(int api, MethodVisitor methodVisitor, String callerClass, String callerMethod, CallChainContext context) {
        super(api, methodVisitor);
        this.callerClass = callerClass;
        this.callerMethod = callerMethod;
        this.context = context;
    }

    /**
     * 核心方法:解析方法调用指令
     * @param opcode 调用指令类型(invokevirtual/invokestatic 等)
     * @param owner 被调用类的 ASM 格式名称(如 com/example/OrderDao)
     * @param name 被调用方法名
     * @param descriptor 方法描述符(如 (Ljava/lang/String;)V)
     * @param isInterface 是否是接口方法
     */
    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
        super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        // 1. 转换被调用类名为全限定名
        String calleeClass = owner.replace("/", ".");
        // 2. 包过滤:只处理指定包下的被调用类
        Set<String> includePackages = context.getIncludePackages();
        if (includePackages != null && !includePackages.isEmpty()) {
            boolean match = includePackages.stream().anyMatch(pkg -> calleeClass.startsWith(pkg));
            if (!match) {
                return;
            }
        }
        // 3. 跳过构造方法/静态初始化方法(可选,根据业务需求调整)
        if ("<init>".equals(name) || "<clinit>".equals(name)) {
            return;
        }
        // 4. 存储调用关系
        MethodCall methodCall = new MethodCall(callerClass, callerMethod, calleeClass, name);
        context.addMethodCall(methodCall);
    }
}

3.4 开发插件入口 Mojo 类

Mojo 是 Maven 插件的核心执行入口类(Mojo = Maven Old Java Object,等价于插件的'main 类'),所有插件逻辑都围绕这个类展开。

创建 AnalyzeCallChainMojo 类

创建 AnalyzeCallChainMojo 类整合「遍历.class 文件 → 触发 ASM 解析 → 输出结果」全流程,代码如下:

/**
 * Maven 插件核心 Mojo:方法调用链分析
 * Goal: analyze(调用命令:mvn call-chain:analyze)
 */
@Mojo(name = "analyze", threadSafe = true, defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE)
@Execute(phase = LifecyclePhase.COMPILE)
public class AnalyzeCallChainMojo extends AbstractMojo {
    // ========== Maven 注入参数 ==========
    /** Maven 项目对象(自动注入) */
    @Parameter(defaultValue = "${project}", readonly = true)
    protected MavenProject project;

    /** 扫描的.class 文件根目录(默认:target/classes) */
    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
    private String classRootDir;

    /** 需扫描的包名列表 */
    @Parameter(name = "scanPackages", property = "scanPackages")
    private List<String> scanPackages = new ArrayList<>();

    /** 排除扫描的包名列表 */
    @Parameter(name = "excludeScanPackages", property = "excludeScanPackages")
    private List<String> excludeScanPackages = new ArrayList<>();

    /** 目标分析类全限定名(必填) */
    @Parameter(name = "targetClass", property = "targetClass", required = true)
    private String targetClass;

    // 核心执行方法(后续实现)
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        // ...
    }
}

该类继承 AbstractMojo(Maven 插件基类),@Mojo 是 Maven 插件开发的核心注解,每个参数都对应插件的核心行为配置:

  • name = "analyze":插件的 Goal 名称,是调用插件的核心标识,调用插件时执行命令 mvn call-chain:analyze。
  • threadSafe = true:标记插件是否线程安全,设置为 true 时,避免 Maven 在并行构建(如 mvn -T 4 clean compile)时报错。
  • defaultPhase = LifecyclePhase.COMPILE:插件默认绑定的 Maven 生命周期阶段,因为插件需要解析编译后的.class 文件,所以必须在编译完成后执行。
  • requiresDependencyResolution = ResolutionScope.COMPILE:指定插件执行前需要解析的依赖范围,Maven 会先解析项目 compile 范围的依赖(如 ASM、Maven Plugin API),再执行插件;确保插件运行时依赖已就绪,避免 ClassNotFound 异常。

@Execute 注解是对 @Mojo 的补充,用于强制 Maven 执行插件之前,会先执行 COMPILE 阶段(编译项目生成.class 文件),避免因跳过编译(如 mvn call-chain:analyze -Dmaven.compile.skip=true)导致插件找不到.class 文件的问题。

@Parameter 是 Maven Plugin API 提供的核心注解,Maven 框架会自动将 pom.xml 中配置的插件参数注入到 Mojo 类的成员变量中。

实现核心执行流程(execute)

execute() 是插件的入口,流程如下:

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
    // 1. 初始化参数,避免空集合 NPE
    scanPackages = Optional.ofNullable(scanPackages).orElseGet(ArrayList::new);
    excludeScanPackages = Optional.ofNullable(excludeScanPackages).orElseGet(ArrayList::new);
    // 2. 校验扫描目录合法性
    validateClassRootDir();
    // 3. 打印扫描配置(便于调试)
    logScanConfig();
    // 4. 解析所有.class 文件,提取调用关系
    Set<MethodCallInfo> allMethodCallInfos = parseAllClassFiles();
    // 5. 构建调用关系映射(调用方→被调用方列表)
    Map<String, List<String>> methodCallRelationMap = buildMethodCallRelationMap(allMethodCallInfos);
    // 6. 筛选目标类的调用关系
    Map<String, List<String>> targetClassCallRelationMap = filterTargetClassCallRelations(methodCallRelationMap);
    // 7. 输出树形调用链结果
    printCallChainResult(targetClass, targetClassCallRelationMap);
}
扫描 class 文件(traverseClassFiles + scanClassFilesRecursive)

递归遍历 classRootDir 目录下所有 .class 文件,收集文件路径:

/**
 * 递归遍历.class 文件,返回所有 class 文件路径
 */
private List<String> traverseClassFiles(String dirPath) throws MojoExecutionException {
    File scanDir = new File(dirPath);
    if (!scanDir.exists() || !scanDir.isDirectory()) {
        throw new MojoExecutionException(String.format("扫描路径非法:%s", dirPath));
    }
    List<String> classFilePaths = new ArrayList<>();
    scanClassFilesRecursive(scanDir, classFilePaths);
    return Collections.unmodifiableList(classFilePaths);
}

/**
 * 递归扫描.class 文件
 */
private void scanClassFilesRecursive(File file, List<String> result) {
    if (file.isDirectory()) {
        File[] childFiles = file.listFiles();
        if (childFiles == null) {
            getLog().warn(String.format("无法访问目录:%s", file.getAbsolutePath()));
            return;
        }
        for (File child : childFiles) {
            scanClassFilesRecursive(child, result);
        }
    } else if (file.getName().endsWith(CLASS_FILE_SUFFIX)) {
        result.add(file.getAbsolutePath());
    }
}
解析 class 文件(parseClassFile)

通过 ASM 的 ClassReader 和自定义 ClassCallVisitor 解析 .class 文件,提取方法调用关系:

/**
 * 解析.class 文件,提取方法调用关系
 */
private Set<MethodCallInfo> parseClassFile(String classFilePath, List<String> scanPackages, List<String> excludeScanPackages) throws MojoExecutionException {
    try (FileInputStream fis = new FileInputStream(classFilePath)) {
        ClassReader classReader = new ClassReader(fis);
        Set<MethodCallInfo> methodCallSet = new HashSet<>();
        // 自定义 ClassVisitor,解析类结构并捕获调用关系
        ClassCallVisitor classVisitor = new ClassCallVisitor(ASM_API_VERSION, methodCallSet, scanPackages, excludeScanPackages);
        // 执行解析:跳过调试信息和栈帧,提升效率
        classReader.accept(classVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
        return methodCallSet;
    } catch (IOException e) {
        String errorMsg = String.format("解析类文件失败:%s", classFilePath);
        throw new MojoExecutionException(errorMsg, e);
    }
}

ClassCallVisitor 是自定义的 ASM Visitor,核心逻辑是遍历方法指令,捕获 INVOKEVIRTUAL/INVOKESTATIC 等调用指令,封装为 MethodCallInfo

构建调用关系(buildMethodCallRelationMap)

根据全量 MethodCallInfo 构建「调用方方法全限定名→被调用方列表」的映射:

/**
 * 构建调用关系映射(调用方→被调用方列表)
 */
public static Map<String, List<String>> buildMethodCallRelationMap(Collection<MethodCallInfo> methodCallInfos) {
    if (Objects.isNull(methodCallInfos) || methodCallInfos.isEmpty()) {
        return Collections.emptyMap();
    }
    return methodCallInfos.stream().distinct()
            // 去重重复的调用关系
            .collect(Collectors.groupingBy(MethodCallInfo::getCallerMethodFull,
                    Collectors.mapping(MethodCallInfo::getCalleeMethodFull, Collectors.toList()))); // 映射值:被调用方列表
}
筛选目标类的调用关系(filterTargetClassCallRelations)

根据 buildMethodCallRelationMap() 构建的调用关系筛查出目标类中所有方法的调用关系:

/**
 * 筛选目标类的调用关系(按类名前缀过滤)
 */
private Map<String, List<String>> filterTargetClassCallRelations(Map<String, List<String>> methodCallRelationMap) {
    if (Objects.isNull(methodCallRelationMap) || methodCallRelationMap.isEmpty()) {
        return Collections.emptyMap();
    }
    String targetClassPrefix = targetClass + METHOD_FULL_NAME_SEPARATOR;
    // 筛选 + 排序
    return methodCallRelationMap.entrySet().stream()
            .filter(entry -> entry.getKey().startsWith(targetClassPrefix))
            .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().stream().sorted().toList(), (v1, v2) -> v1, TreeMap::new));
}
树形输出与循环检测(printMethodCallChain)

递归打印树形调用链,并通过 visitedMethods 集合检测循环调用:

/**
 * 递归打印方法调用链
 */
private void printMethodCallChain(String currentMethod, List<String> calleeList, String prefix, boolean isLastNode, Map<String, List<String>> relationMap, Set<String> visitedMethods) {
    // 空值保护(避免 NPE)
    if (Objects.isNull(currentMethod) || currentMethod.trim().isEmpty()) {
        return;
    }

    // ========== 循环调用检测(传递引用 + 回溯) ==========
    if (visitedMethods.contains(currentMethod)) {
        String symbol = isLastNode ? TREE_LAST_NODE_SYMBOL : TREE_NODE_SYMBOL;
        getLog().info(String.format("%s%s%s%s", prefix, symbol, currentMethod, " 「循环调用」"));
        return;
    }

    // 标记当前方法为已访问
    visitedMethods.add(currentMethod);

    // ========== 打印当前节点 ==========
    String nodeSymbol = isLastNode ? TREE_LAST_NODE_SYMBOL : TREE_NODE_SYMBOL;
    getLog().info(String.format("%s%s%s", prefix, nodeSymbol, currentMethod));

    // ========== 处理子节点 ==========
    List<String> parsedCalleeList = Optional.ofNullable(calleeList).map(ArrayList::new)
            // 避免修改原列表
            .orElse(new ArrayList<>()).stream()
            .filter(Objects::nonNull)
            // 过滤 null 的被调用方
            .sorted(Comparator.naturalOrder())
            .toList();

    if (parsedCalleeList.isEmpty()) {
        // 回溯:移除当前方法,避免影响兄弟节点检测
        visitedMethods.remove(currentMethod);
        return;
    }

    // 构建子节点前缀
    String childPrefix = prefix + (isLastNode ? TREE_LAST_CHILD_PREFIX : TREE_CHILD_PREFIX);

    // 遍历子节点递归打印
    for (int i = 0; i < parsedCalleeList.size(); i++) {
        String calleeMethod = parsedCalleeList.get(i);
        boolean isLastChild = (i == parsedCalleeList.size() - 1);
        List<String> nextCalleeList = Optional.ofNullable(relationMap.get(calleeMethod)).orElse(Collections.emptyList());
        // 传递同一个 visitedMethods 引用(核心:实现深层循环检测)
        printMethodCallChain(calleeMethod, nextCalleeList, childPrefix, isLastChild, relationMap, visitedMethods);
    }

    // ========== 回溯:移除当前方法,完成循环检测闭环 ==========
    visitedMethods.remove(currentMethod);
}

核心逻辑:

  • 通过 visitedMethods 集合记录已访问的方法,检测到循环调用时标记并终止递归;
  • 递归遍历子节点时,通过 prefix 控制树形缩进(如 ├──/└──),保证输出格式清晰;
  • 回溯移除当前方法,确保兄弟节点的循环检测不受影响。

四、插件测试与使用

4.1 安装插件到本地仓库

在插件项目根目录执行:

mvn clean install

4.2 在业务项目中引入插件

在需要解析调用链的业务项目 pom.xml 中添加插件配置:

<build>
    <plugins>
        <plugin>
            <groupId>com.shijie.plugin</groupId>
            <artifactId>call-chain-maven-plugin</artifactId>
            <version>1.0.0</version>
            <configuration>
                <!-- 必填:目标分析类全限定名 -->
                <targetClass>com.shijie.service.UserService</targetClass>
                <!-- 可选:扫描的包名列表 -->
                <scanPackages>
                    <scanPackage>com.shijie.service</scanPackage>
                    <scanPackage>com.shijie.dao</scanPackage>
                </scanPackages>
                <!-- 可选:排除的包名列表 -->
                <excludeScanPackages>
                    <excludeScanPackage>com.shijie.test</excludeScanPackage>
                </excludeScanPackages>
            </configuration>
        </plugin>
    </plugins>
</build>

4.3 执行插件

打开终端执行以下命令:

mvn call-chain-analyzer:analyze -f pom.xml

或在 IDEA 右侧的 Maven 面板「插件」中找到该插件,双击执行。

插件会在控制台输出目标类的方法调用链。

目录

  1. 基于 ASM+Maven 插件实现 Java 方法调用链分析
  2. 一、前置准备
  3. 1.1 开发环境
  4. 1.2 ASM 核心认知
  5. 二、插件核心原理
  6. 三、Maven 插件开发
  7. 3.1 初始化 Maven 插件项目
  8. 方式一:用 Archetype 创建插件项目
  9. 方式二:创建普通 Java 项目
  10. 3.2 定义数据模型(MethodCallInfo)
  11. 3.3 实现 ASM 解析逻辑
  12. 类级别访问器(ClassCallVisitor)
  13. 方法级别访问器(MethodCallVisitor)
  14. 3.4 开发插件入口 Mojo 类
  15. 创建 AnalyzeCallChainMojo 类
  16. 实现核心执行流程(execute)
  17. 扫描 class 文件(traverseClassFiles + scanClassFilesRecursive)
  18. 解析 class 文件(parseClassFile)
  19. 构建调用关系(buildMethodCallRelationMap)
  20. 筛选目标类的调用关系(filterTargetClassCallRelations)
  21. 树形输出与循环检测(printMethodCallChain)
  22. 四、插件测试与使用
  23. 4.1 安装插件到本地仓库
  24. 4.2 在业务项目中引入插件
  25. 4.3 执行插件
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 二分查找实战:山峰数组的峰顶索引与寻找峰值
  • 多路双向串口转网口上位机 C++ 源码及 Socket 通信实现
  • MySQL 与 MCP 集成:从环境构建到 AI 驱动的数据交互
  • 5 款值得关注的国产 AI 大模型功能评测
  • 大模型分布式训练与高效调参技术实战
  • Docker核心概念与架构
  • Spring AI Alibaba 深度解析:Java 构建企业级 AI 应用框架指南
  • OpenClaw Memory 本地模式配置:Ubuntu+CUDA+cuDNN+llama.cpp
  • 基于 Q-learning 的无人机三维路径规划原理及 MATLAB 实现
  • Spring ShedLock 指南
  • Java 网络编程:TCP 流套接字编程
  • Whisper-large-v3 语音识别模型部署与会议转录实测
  • AIGC 视频生成成本优化实战:文字 + 图片输入下 20 秒与 30 秒模型选型与价格对比
  • 鸿蒙金融理财全栈项目:架构设计、数据安全与体验优化
  • Python 中的 with 语句与 try 语句:资源管理对比
  • OpenClaw 中文发行版部署指南:npm/Docker 多模式安装与配置
  • whisper-large-v3-turbo 模型一键部署指南
  • 修复 Anaconda 开始菜单快捷方式丢失及 mkmenus 报错
  • Z 字形变换与外观数列算法实战解析
  • VSCode Copilot 配置文件提示未知工具警告解析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online