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天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
106 29
|
2月前
|
设计模式 前端开发 Java
步步深入SpringMvc DispatcherServlet源码掌握springmvc全流程原理
通过对 `DispatcherServlet`源码的深入剖析,我们了解了SpringMVC请求处理的全流程。`DispatcherServlet`作为前端控制器,负责请求的接收和分发,处理器映射和适配负责将请求分派到具体的处理器方法,视图解析器负责生成和渲染视图。理解这些核心组件及其交互原理,有助于开发者更好地使用和扩展SpringMVC框架。
66 4
|
3月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
230 2
|
2月前
|
XML 安全 Java
Spring Boot中使用MapStruct进行对象映射
本文介绍如何在Spring Boot项目中使用MapStruct进行对象映射,探讨其性能高效、类型安全及易于集成等优势,并详细说明添加MapStruct依赖的步骤。
|
4月前
|
设计模式 前端开发 Java
Spring MVC——项目创建和建立请求连接
MVC是一种软件架构设计模式,将应用分为模型、视图和控制器三部分。Spring MVC是基于MVC模式的Web框架,通过`@RequestMapping`等注解实现URL路由映射,支持GET和POST请求,并可传递参数。创建Spring MVC项目与Spring Boot类似,使用`@RestController`注解标记控制器类。
63 1
Spring MVC——项目创建和建立请求连接
|
4月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
4月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
77 2
|
4月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
319 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
4月前
|
前端开发 Java
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
文章介绍了如何使用SpringBoot创建简单的后端服务器来处理HTTP请求,包括建立连接、编写Controller处理请求,并返回响应给前端或网址。
77 0
学习SpringMVC,建立连接,请求,响应 SpringBoot初学,如何前后端交互(后端版)?最简单的能通过网址访问的后端服务器代码举例
|
Java Spring 前端开发
【spring Boot】Spring中@Controller和@RestController之间的区别
spring Boot入手的第一天,看到例子中的@RestController ............. 相同点:都是用来表示Spring某个类的是否可以接收HTTP请求 不同点:@Controller标识一个Spring类是Spring MVC controller处理器     @RestController:  a convenience annotation that does nothing more than adding the@Controller and @ResponseBody annotations。
1433 0