Spring Web中获取@RestController完整URL及关联基础URL的方法
如何在Spring Web中获取@RestController的完整URL及基础路径
我来帮你搞定这两个问题,结合你给出的ServiceEnumerator代码示例,下面是具体的实现方案:
一、获取单个@RestController对应的完整URL
要拿到控制器对应的完整URL,得把三个部分拼起来:
- 应用的上下文路径(如果配置了
server.servlet.context-path的话) - 控制器类上的类级别请求映射路径(比如
@RequestMapping("/api")) - 控制器方法上的方法级别请求映射路径(比如
@GetMapping("/users/{id}"))
举个实际例子:
@RestController @RequestMapping("/api/users") public class UserController { @GetMapping("/{id}") public User getUser(@PathVariable Long id) { // 业务逻辑 } }
如果你的应用上下文路径是/my-app,那这个方法的完整URL就是/my-app/api/users/{id}。
二、通过ApplicationContext获取所有@RestController的基础URL
你已经通过context.getBeansWithAnnotation(RestController.class)拿到了所有RestController Bean,接下来有两种靠谱的方式获取它们的基础URL:
方式1:反射读取类上的请求映射注解
直接通过反射读取控制器类上的@RequestMapping(或它的派生注解)的路径值,代码可以这样改:
@Component public class ServiceEnumerator { @Autowired private ApplicationContext context; // 注入上下文路径,默认是空字符串(没配置的情况) @Value("${server.servlet.context-path:}") private String contextPath; @PostConstruct private void postConstruct() { final Map<String, Object> restControllers = context.getBeansWithAnnotation(RestController.class); for (Object controllerBean : restControllers.values()) { Class<?> controllerClass = controllerBean.getClass(); // 处理Spring生成的CGLIB代理类,拿到真实的控制器类 if (controllerClass.getName().contains("$$EnhancerBySpringCGLIB$$")) { controllerClass = controllerClass.getSuperclass(); } // 获取类级别的@RequestMapping注解(包括@GetMapping等派生注解) RequestMapping classLevelMapping = controllerClass.getAnnotation(RequestMapping.class); if (classLevelMapping != null) { String[] basePaths = classLevelMapping.value(); for (String basePath : basePaths) { // 拼接上下文路径和基础路径,处理重复斜杠的情况 String fullBaseUrl = contextPath + basePath; fullBaseUrl = fullBaseUrl.replaceAll("//", "/"); System.out.println("RestController基础URL前缀:" + fullBaseUrl); } } else { // 如果没有类级别映射,基础路径就是上下文路径(或根路径) String fullBaseUrl = contextPath.isEmpty() ? "/" : contextPath; System.out.println("RestController基础URL前缀:" + fullBaseUrl); } } } }
方式2:用Spring的RequestMappingHandlerMapping(更推荐)
Spring的RequestMappingHandlerMapping维护了所有请求映射的元数据,包括类和方法的完整路径,用它能更准确地获取信息,还能自动处理代理类等问题:
@Component public class ServiceEnumerator { @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping; @PostConstruct private void postConstruct() { // 获取所有已注册的请求映射 Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods(); for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) { RequestMappingInfo mappingInfo = entry.getKey(); HandlerMethod handlerMethod = entry.getValue(); // 判断当前方法所属的类是否是@RestController if (handlerMethod.getBeanType().isAnnotationPresent(RestController.class)) { // 获取所有路径模式(包括方法级别的) Set<String> pathPatterns = mappingInfo.getPathPatternsCondition().getPatterns(); // 读取上下文路径 String contextPath = requestMappingHandlerMapping.getApplicationContext() .getEnvironment().getProperty("server.servlet.context-path", ""); for (String pathPattern : pathPatterns) { String fullUrl = contextPath + pathPattern; fullUrl = fullUrl.replaceAll("//", "/"); System.out.println("完整请求URL:" + fullUrl); System.out.println("所属控制器:" + handlerMethod.getBeanType().getSimpleName()); } } } } }
这种方式的好处:
- 自动处理代理类,不用手动判断父类
- 直接拿到包含方法级别的完整路径,支持路径变量、通配符等
- 能准确获取Spring解析后的路径规则
几个关键注意事项
- 上下文路径不要忘:如果你的应用配置了
server.servlet.context-path,一定要拼接进去,否则得到的是相对路径 - 代理类处理:Spring可能会给Bean生成CGLIB代理,这时候直接
getClass()拿到的是代理类,要切换到父类才能读取到真实的注解信息 - 派生注解兼容:类级别如果用了
@GetMapping、@PostMapping这类派生注解,getAnnotation(RequestMapping.class)依然能读取到路径,因为它们本身就包含@RequestMapping的元数据
内容的提问来源于stack exchange,提问作者Jonathan S. Fisher
相关产品推荐
相关产品推荐

