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是怎么区分固定路径和其他路径的吗?

目录
相关文章
|
9天前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
24 4
|
27天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
102 2
|
14天前
|
XML 安全 Java
Spring Boot中使用MapStruct进行对象映射
本文介绍如何在Spring Boot项目中使用MapStruct进行对象映射,探讨其性能高效、类型安全及易于集成等优势,并详细说明添加MapStruct依赖的步骤。
|
2月前
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
50 1
Spring MVC——项目创建和建立请求连接
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
2月前
|
前端开发 Java Spring
【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart
【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart
41 2
|
7月前
|
设计模式 前端开发 Java
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
121 0
|
前端开发 Java 程序员
38SpringMVC - SpringMVC架构
38SpringMVC - SpringMVC架构
53 0
|
2月前
|
存储 设计模式 前端开发
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
文章解释了SpringMVC的概念和各部分功能,探讨了应用分层的原因和具体实施的三层架构,以及SpringMVC与三层架构之间的关系和联系。
26 1
什么是SpringMVC?简单好理解!什么是应用分层?SpringMVC与应用分层的关系? 什么是三层架构?SpringMVC与三层架构的关系?
|
6月前
|
安全 前端开发 Java
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
挑战5分钟内基于Springboot+SpringMVC+Mybatis-plus快速构建web后端三层架构
59 1