spring mvc 一次请求如何映射到对应的controller 如何规避冲突

简介: spring mvc 一次请求如何映射到对应的controller 如何规避冲突

spring mvc 一次请求如何映射到对应的controller 如何规避冲突

映射的分析很多了,但是那些看起来冲突的url映射,spring都能正常工作,好奇,故分析一波

看似冲突的URL映射

  • /test/hello
  • /test/{res}

这两个映射URL,如果输入**/test/hello**,那么两个映射规则应该都是符合的,那么它会走哪一个,是不是随机的?为啥不冲突?

测试代码

创建一个带有spring mvc的简单的spring boot工程,加入下面这个controller文件

package com.example.download;
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;
@RestController
@RequestMapping("/test")
public class MappingTestApi {
    @GetMapping("/hello")
    public String sayHello() {
        return "HELLO.";
    }
    @GetMapping("/{res}")
    public String sayRes(@PathVariable String res) {
        return res;
    }
}

测试访问

  • 访问1:/test/rese
wanglh@dark:~/Desktop$ curl http://localhost:8080/test/rese
resewanglh@dark:~/Desktop$

显然,命中的是@GetMapping("/{res}"),意料之中。

  • 访问2:/test/hello
wanglh@dark:~/Desktop$ curl http://localhost:8080/test/hello
HELLO.wanglh@dark:~/Desktop$

显然,命中了@GetMapping("/hello"),那么为什么它不会命中@GetMapping("/{res}"),输出hello呢?

源码分析

spring mvc 的请求入口 在org.springframework.web.servlet.DispatcherServlet#doDispatch,首先在这边打断点,往下走。 这里就省略了。

我们直接上核心获取映射的方法源码:

/**
   * Look up the best-matching handler method for the current request.
   * If multiple matches are found, the best match is selected.
   * @param lookupPath mapping lookup path within the current servlet mapping
   * @param request the current request
   * @return the best-matching handler method, or {@code null} if no match
   * @see #handleMatch(Object, String, HttpServletRequest)
   * @see #handleNoMatch(Set, String, HttpServletRequest)
   */
  @Nullable
  protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
        // 这里获取直接匹配的路径
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
      addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
            // 这里获取匹配的路径
      addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    if (!matches.isEmpty()) {
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        bestMatch = matches.get(0);
        // 省略
      }
      request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.getHandlerMethod();
    }
    else {
      return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    }
  }
  1. this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    这个方法获取的是写死的直接路径映射,举个例子"/test/aaaa"就属于,而"/test/{aaaa}“这种包含”{}"路径变量的URL则不算。
  2. addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    这个方法是当第一步找不到写死直接路径映射的时,将所有注册的映射进行匹配,这个mappingRegistry既包含"/test/aaaa",也包含"/test/{aaaa}“这样的映射,当然还有第三方包注入的路径,我这里比较干净,只有spring mvc额外提供的”/error"。

结论

  1. 通过DEBUG可以发现,“/test/hello”这样固定的路径,会在第一步this.mappingRegistry.getMappingsByDirectPath(lookupPath);中直接找到映射,不会再去匹配下面的全量映射了。
  2. 如果不符合第一步的映射,第二步匹配时,自然也不会命中第一步中那些直接路径了。

留个问题

我这边没有展示this.mappingRegistry.getMappingsByDirectPath(lookupPath);进去的源码,你知道spring mvc是怎么区分固定路径和其他路径的吗?

目录
相关文章
|
17天前
|
JSON 前端开发 Java
Spring MVC返回JSON数据
综上所述,Spring MVC提供了灵活、强大的方式来支持返回JSON数据,从直接使用 `@ResponseBody`及 `@RestController`注解,到通过配置消息转换器和异常处理器,开发人员可以根据具体需求选择合适的实现方式。
41 4
|
17天前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
43 3
|
18天前
|
XML 前端开发 Java
Spring MVC接收param参数(直接接收、注解接收、集合接收、实体接收)
Spring MVC提供了灵活多样的参数接收方式,可以满足各种不同场景下的需求。了解并熟练运用这些基本的参数接收技巧,可以使得Web应用的开发更加方便、高效。同时,也是提高代码的可读性和维护性的关键所在。在实际开发过程中,根据具体需求选择最合适的参数接收方式,能够有效提升开发效率和应用性能。
41 2
|
1月前
|
前端开发 Java 应用服务中间件
我以为我对Spring MVC很了解,直到我遇到了...
所有人都知道Spring MVC是是开发的,却鲜有人知道Spring MVC的理论基础来自于1978 年提出MVC模式的一个老头子,他就是Trygve Mikkjel Heyerdahl Reenskaug,挪威计算机科学家,名誉教授。Trygve Reenskaug的MVC架构思想早期用于图形用户界面(GUI) 的软件设计,他对MVC是这样解释的。MVC 被认为是解决用户控制大型复杂数据集问题的通用解决方案。最困难的部分是为不同的架构组件想出好的名字。模型-视图-编辑器是第一个。
我以为我对Spring MVC很了解,直到我遇到了...
|
22天前
|
前端开发 Java API
Spring Boot 中的 MVC 支持
### Spring Boot 注解摘要 - **@RestController** - **@RequestMapping** - **@PathVariable** - **@RequestParam** - **@RequestBody**
21 2
|
23天前
|
Java Spring
spring restTemplate 进行http请求的工具类封装
spring restTemplate 进行http请求的工具类封装
37 3
|
6天前
|
前端开发 Java Spring
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
18 0
|
21天前
|
JSON 前端开发 Java
Spring Boot中的MVC支持
本节课主要讲解了 Spring Boot 中对 MVC 的支持,分析了 @RestController、 @RequestMapping、@PathVariable、 @RequestParam 和 @RequestBody 四个注解的使用方式,由于 @RestController 中集成了 @ResponseBody 所以对返回 json 的注解不再赘述。以上四个注解是使用频率很高的注解,在所有的实际项目中基本都会遇到,要熟练掌握。
|
26天前
|
缓存 前端开发 Java
SpringMVC原理(1)-文件上传请求
【7月更文挑战第2天】SpringMVC文件上传请求原理:文件上传请求的执行流程、文件上传的自动配置原理 涉及组件:MultiPartFile、MultipartResolver、MultipartHttpServlet
|
1月前
|
XML 前端开发 Java
Spring Boot与Spring MVC的区别和联系
Spring Boot与Spring MVC的区别和联系