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

Spring REST端点能否获取自身URL模板?Tomcat调用场景下的实现

如何让Spring RESTful端点获取自身的URL模板?

当然可以!Spring给我们提供了几种靠谱的方式,能让REST端点轻松拿到自身匹配的URL模板,完美适配你生成RabbitMQ交换器名称的需求。比如你提到的GET /my_resource/123/请求,我们可以准确获取到对应的GET /my_resource/{id}/模板,再转换成get|my_resource.id这类格式的交换器名称。

方法1:借助RequestMappingHandlerMapping获取实际映射信息

这是最可靠的方式,因为它直接从Spring的请求映射注册表中读取实际匹配的模板,不管你用了占位符、正则还是Ant风格路径,都能准确拿到。

代码示例

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;

@RestController
@RequestMapping("/my_resource")
public class MyResourceController {

    private final RequestMappingHandlerMapping handlerMapping;

    // 通过构造注入RequestMappingHandlerMapping
    public MyResourceController(RequestMappingHandlerMapping handlerMapping) {
        this.handlerMapping = handlerMapping;
    }

    @GetMapping("/{id}/")
    public ResponseEntity<String> getResource(@PathVariable Long id, HttpServletRequest request) throws Exception {
        // 获取当前请求对应的HandlerMethod(即当前执行的控制器方法)
        HandlerMethod handlerMethod = (HandlerMethod) handlerMapping.getHandler(request).getHandler();
        // 获取该方法对应的RequestMappingInfo,里面包含了所有映射规则
        RequestMappingInfo mappingInfo = handlerMapping.getMappingForMethod(handlerMethod.getMethod(), handlerMethod.getBeanType());
        
        // 提取HTTP请求方法(比如GET)
        RequestMethodsRequestCondition methodsCondition = mappingInfo.getMethodsCondition();
        String httpMethod = methodsCondition.getMethods().iterator().next().name();
        
        // 提取URL模板(比如/my_resource/{id}/)
        PatternsRequestCondition patternsCondition = mappingInfo.getPatternsCondition();
        String urlTemplate = patternsCondition.getPatterns().iterator().next();
        
        // 转换成你需要的交换器名称格式:get|my_resource.id
        String exchangeName = String.format("%s|%s", 
            httpMethod.toLowerCase(), 
            urlTemplate.replace("/", ".").replace("{id}", "id").replaceAll("^\\.|\\.$", "")
        );
        
        // 这里就可以用exchangeName发送消息到RabbitMQ了
        return ResponseEntity.ok("生成的交换器名称:" + exchangeName);
    }
}

逻辑说明

  1. 注入RequestMappingHandlerMapping:这个组件负责管理所有@RequestMapping注解的映射规则。
  2. 通过当前请求拿到对应的HandlerMethod:定位到正在执行的控制器方法。
  3. 从RequestMappingInfo中提取HTTP方法和URL模板:这是Spring内部实际使用的映射规则,准确性拉满。
  4. 格式化模板生成交换器名称:替换特殊字符,把{id}转换成id,去掉首尾多余的.,得到你想要的格式。

方法2:通过反射直接读取注解信息

如果你的控制器路径配置比较简单,也可以直接通过反射读取类和方法上的@RequestMapping系列注解,拼接出URL模板。

代码示例

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import java.lang.reflect.Method;

@RestController
@RequestMapping("/my_resource")
public class MyResourceController {

    @GetMapping("/{id}/")
    public ResponseEntity<String> getResource(@PathVariable Long id, HttpServletRequest request) {
        // 通过匿名内部类获取当前执行的方法
        Method currentMethod = new Object() {}.getClass().getEnclosingMethod();
        
        // 读取类上的@RequestMapping路径
        RequestMapping classMapping = this.getClass().getAnnotation(RequestMapping.class);
        String classPath = classMapping.value()[0];
        
        // 读取方法上的@GetMapping路径
        GetMapping methodMapping = currentMethod.getAnnotation(GetMapping.class);
        String methodPath = methodMapping.value()[0];
        
        // 拼接完整URL模板
        String urlTemplate = classPath + methodPath;
        String httpMethod = "GET";
        
        // 生成交换器名称
        String exchangeName = String.format("%s|%s", 
            httpMethod.toLowerCase(), 
            urlTemplate.replace("/", ".").replace("{id}", "id").replaceAll("^\\.|\\.$", "")
        );
        
        return ResponseEntity.ok("生成的交换器名称:" + exchangeName);
    }
}

优缺点

  • 优点:实现简单,不需要注入额外组件。
  • 缺点:如果用了复杂的路径配置(比如继承的注解、动态路径规则),可能无法准确获取到实际的匹配模板,不如第一种方法通用。

注意事项

  • 如果你的应用中有多个HandlerMapping,一定要确保注入的是RequestMappingHandlerMapping,这是专门处理@RequestMapping注解的组件。
  • 生成交换器名称时,要注意处理特殊字符,避免RabbitMQ不支持的字符(比如/、{}),可以根据实际需求调整格式化规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:17:30