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); } }
逻辑说明
- 注入
RequestMappingHandlerMapping:这个组件负责管理所有@RequestMapping注解的映射规则。 - 通过当前请求拿到对应的
HandlerMethod:定位到正在执行的控制器方法。 - 从
RequestMappingInfo中提取HTTP方法和URL模板:这是Spring内部实际使用的映射规则,准确性拉满。 - 格式化模板生成交换器名称:替换特殊字符,把
{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
相关产品推荐
相关产品推荐

