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

Spring Boot启动时如何获取所有请求方法、路径、头及请求体?

Spring Boot启动后预扫描端点并生成信息文件的实现方案

需求肯定能实现

Spring Boot提供了上下文生命周期钩子,可以在应用完全启动、任何端点被访问前触发端点扫描逻辑,还能通过框架提供的API获取所有端点的元数据(请求方法、路径、头信息),请求体的信息可以通过方法参数注解识别。

现有代码的问题

你的代码没法正常工作,主要有这几个问题:

  • 判断请求体的逻辑不准确:只靠方法参数数量>0来判断是否接收请求体,很多端点有参数但不是请求体(比如@RequestParam),应该直接检查参数上的@RequestBody注解
  • 注解获取不全面:只获取@RequestMapping注解,但实际开发中常用@PostMapping、@PutMapping这类派生注解,这些注解本身包含@RequestMapping,但用getMethodAnnotation会直接拿不到,得用findAnnotation
  • 没有自动触发逻辑:当前类只是@Component,但scanEndpointsWithRequestBody方法没人调用,得绑定到应用启动完成的事件上
  • 缺少文件生成逻辑:代码只是打印路径,没实现写入文件的功能

修正后的完整实现

下面是可以直接用的代码,包含启动自动触发、准确识别端点信息、生成文件的逻辑:

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Set;

@Component
public class EndpointScanner implements ApplicationListener<ContextRefreshedEvent> {

    private final RequestMappingHandlerMapping handlerMapping;

    public EndpointScanner(RequestMappingHandlerMapping handlerMapping) {
        this.handlerMapping = handlerMapping;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 避免上下文刷新多次触发(比如父子上下文)
        if (event.getApplicationContext().getParent() == null) {
            scanAndGenerateEndpointFile();
        }
    }

    private void scanAndGenerateEndpointFile() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        StringBuilder content = new StringBuilder();
        content.append("所有端点信息:\n");
        content.append("========================================\n");

        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            RequestMappingInfo info = entry.getKey();
            HandlerMethod handlerMethod = entry.getValue();

            // 获取请求方法(GET/POST/PUT等)
            Set<String> httpMethods = info.getMethodsCondition().getMethods();
            if (httpMethods.isEmpty()) {
                httpMethods.add("ANY");
            }

            // 请求路径
            Set<String> paths = info.getPathsCondition().getPatterns();

            // 请求头条件
            Set<String> headers = info.getHeadersCondition().getExpressions();

            // 判断是否接收请求体
            boolean acceptsRequestBody = false;
            for (Parameter parameter : handlerMethod.getMethod().getParameters()) {
                if (parameter.isAnnotationPresent(RequestBody.class)) {
                    acceptsRequestBody = true;
                    break;
                }
            }

            // 拼接信息
            content.append("请求方法: ").append(String.join(", ", httpMethods)).append("\n");
            content.append("请求路径: ").append(String.join(", ", paths)).append("\n");
            content.append("请求头条件: ").append(headers.isEmpty() ? "无" : String.join(", ", headers)).append("\n");
            content.append("是否接收请求体: ").append(acceptsRequestBody ? "是" : "否").append("\n");
            content.append("处理方法: ").append(handlerMethod.getMethod().getDeclaringClass().getName())
                    .append(".").append(handlerMethod.getMethod().getName()).append("\n");
            content.append("========================================\n");
        }

        // 写入文件
        try (FileWriter writer = new FileWriter("endpoints_info.txt")) {
            writer.write(content.toString());
            System.out.println("端点信息文件已生成: endpoints_info.txt");
        } catch (IOException e) {
            System.err.println("生成端点文件失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

关键说明

  1. 启动触发时机:实现ApplicationListener<ContextRefreshedEvent>,在Spring上下文完全刷新后执行扫描,加了getParent() == null判断,避免嵌入式容器的父子上下文重复触发
  2. 准确识别请求体:遍历方法参数,检查是否有@RequestBody注解,这是判断端点是否接收请求体的标准方式
  3. 全面获取元数据:从RequestMappingInfo中直接拿请求方法、路径、头条件,不管用的是@RequestMapping还是派生注解都能覆盖
  4. 文件生成:用FileWriter写入文本文件,你可以根据需求改成JSON、XML等格式,或者调整输出内容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:27:43