【老王读SpringMVC-2】url 与 controller method 的映射关系注册

这篇具有很好参考价值的文章主要介绍了【老王读SpringMVC-2】url 与 controller method 的映射关系注册。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上文提到,如果我们自己要实现 spring mvc 框架的话,大致需要实现如下功能:

  • 0、将 url 与 Controller method 的对应关系进行注册
  • 1、通过请求的 url 找到 Controller method (即 url 与 Controller method 的映射)
  • 2、将请求参数进行绑定,即将入参绑定到 Controller method 的参数对象上
  • 3、执行处 Controller method (即 HandlerAdapter#handle())
  • 4、对 Controller method 的返回值进行处理
    4.1 如果正常返回的话,对返回值对象进行处理(即 ReturnValueHandler)
    包括:如果返回视图 View 的话,对视图进行渲染 (即 ViewResolver)
    4.2 如果有异常返回的话,对异常进行处理(即 @ExceptionHandler)

下面我们就来研究一下,Spring MVC是如何将 url 与 controller method 的映射关系找出来进行注册的?

分析 url 与 handler method 的映射关系的注册

经过前面对 DispatcherServlet#doDispatch() 的分析,我们知道断点应该打在获取 HandlerExecutionChain 的地方。

/**
 * 将所有的 HandlerMapping 按顺序遍历一次,获取 request 对应的 HandlerExecutionChain。
 */
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

可以看到,在获取 HandlerExecutionChain 时,Spring 会将所有的 HandlerMapping 按顺序遍历一次。
而在 Spring 中有许多 HandlerMapping,最常用的当属 RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerMapping

HandlerMapping 是用来定义 request 和处理程序(handler)之间的映射关系的。

Spring 内置了两个常用的实现: BeanNameUrlHandlerMappingRequestMappingHandlerMapping
用户可以编写自定义的 HandlerMapping,并通过实现 org.springframework.core.Ordered 来指定优先级。

下面我们来看下 HandlerMapping 的类图:

【老王读SpringMVC-2】url 与 controller method 的映射关系注册

SpringBoot 默认注册的 HandlerMapping 有:

  • RequestMappingHandlerMapping – 处理 @RequestMapping 注解的 url 与处理程序的映射 (最常用)
  • BeanNameUrlHandlerMapping – 将 / 开头的 beanName 与 url 进行映射
  • RouterFunctionMapping
  • SimpleUrlHandlerMapping – 处理普通的 url
  • WelcomePageHandlerMapping – 处理首页

RequestMappingHandlerMapping 注册映射关系

在 SpringMVC 中,我们最常用的定义端点接口的方式是使用 @RequestMapping
所以,我们主要来研究一下注解形式的 url 是如何进行映射关系注册的?

在 SpringMVC 中,RequestMappingHandlerMapping 是用来支持 @RequestMapping 注解形式的 url 的。
映射关系的注册是在它的父类 AbstractHandlerMethodMapping 中完成的:

【老王读SpringMVC-2】url 与 controller method 的映射关系注册

可以看到,当 RequestMappingHandlerMapping 这个 bean 在加载的时候,会调用父类的 AbstractHandlerMethodMapping#afterPropertiesSet() 来完成 url 与 handler method 映射关系的注册。
具体的注册是由 AbstractHandlerMethodMapping.MappingRegistry#registry() 来完成的。

判断哪些类是 handler:RequestMappingHandlerMapping#isHandler()

/**
 * bean class 上如果有 @Controller 或 @RequestMapping 的话,就认为是一个 handler 处理程序  
 */
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

具体的 register 注册逻辑

request 和 handler method 映射关系的注册是由 AbstractHandlerMethodMapping.MappingRegistry 来实现的。

MappingRegistry 的定义如下:

class MappingRegistry {
    
    // 保存所有的 <RequestMappingInfo, MappingRegistration>  
    private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
    
    // 保存 RequestMappingInfo 中拥有 directPath 的: <directPath, RequestMappingInfo>
    private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
    
    // 保存所有的 HandlerMethod name: <name, List<HandlerMethod>>
    private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
    
    // 保存拥有 cors 配置的 HandlerMethod: <HandlerMethod, CorsConfiguration>
    private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
    .....
}

AbstractHandlerMethodMapping.MappingRegistry#register() 是负责具体的注册逻辑的:
【老王读SpringMVC-2】url 与 controller method 的映射关系注册

可以看到,这里注册了 4 种信息:
1、将 directPath 注册到 pathLookup 中
2、将 HandlerMethod 的 name 注册到 nameLookup 中
3、将 HandlerMethod 注册到 corsLookup 中(处理跨域请求映射)
4、将所有的 RequestMappingInfo 全部都注册到 registry 中

directPath: 指那些没有特殊字符的 path。特殊字符:?、*、{}

补充:SpringBoot 中 RequestMappingHandlerMapping bean 是在哪里定义的?
WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerMapping() 中定义的:

// org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration#requestMappingHandlerMapping
@Bean
@Primary
public RequestMappingHandlerMapping requestMappingHandlerMapping(
        @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
        @Qualifier("mvcConversionService") FormattingConversionService conversionService,
        @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
    // Must be @Primary for MvcUriComponentsBuilder to work
    return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
            resourceUrlProvider);
}

找到 bean 定义处的技巧:
断点打在 bean class 的构造函数或初始化方法里面,当断点进入时,可以很方便的从调用堆栈中找到相应的 BeanDefinition 的值,BeanDefinition 中就记录了这个 bean 是从在哪里定义的。
如果断点打不到 bean class 里面的话,那么就可以在 applicationContext 中获取相应的 BeanDefinition,再查看 bean 定义的地方。
核心就是要找到 bean 对应的 BeanDefinition。

小结

在 SpringMVC 中,request 与 handler method 的请求关系注册和映射都是通过 HandlerMapping 来完成的。
HandlerMapping 的实现类中最常用的是 RequestMappingHandlerMapping,它是用来处理 @RequestMapping 注解形式的请求关系映射的。
映射关系的注册是在它的父类 AbstractHandlerMethodMapping#registerHandlerMethod() 中完成的。
这里注册了 4 种信息:
1、将 directPath 注册到 pathLookup 中
2、将 HandlerMethod 的 name 注册到 nameLookup 中
3、将 HandlerMethod 注册到 corsLookup 中(处理跨域请求映射)
4、将所有的 RequestMappingInfo 全部都注册到 registry 中文章来源地址https://www.toymoban.com/news/detail-425647.html

到了这里,关于【老王读SpringMVC-2】url 与 controller method 的映射关系注册的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • IDEA根据url快速定位Controller方法

    如果你在维护别人的代码,如何快速的定位到接口的代码呢? 比如我想找 /open/novel/daliuta/getWarningList 这个接口对应的Controller的代码。 我这里使用的开发工具是IntelliJ IDEA 2023.1.2 如下方法可以快速定位: 连按两次 shift ,可以调出一个搜索框。在搜索框中输入你的接口地址:

    2024年02月10日
    浏览(61)
  • SpringMVC <url-pattern/>解读

    2.3.1 使用 mvc:default-servlet-handler/ 2.3.2 使用 mvc:resources/(常用)

    2024年02月10日
    浏览(21)
  • SpringMVC中Controller层获取前端请求参数的几种方式

    在controller层方法中定义形参,若请求时参数名、参数类型相同,则SpringMVC会自动绑定。 下面使用PostMan进行测试发现,无论是将参数直接拼接到URL还是写在form-data里;只要参数类型和参数名与方法形参相同,则会自动绑定 当controller层中的方法形参名与请求参数名不同时,可

    2024年02月11日
    浏览(55)
  • SpringMVC框架中@Controller类的方法的返回值的详细介绍

    目录 前言 1. 返回值类型为ModelAndView 2.  返回值为String(视图) 3.  返回值为void 4.  返回值为Object 5. 返回值为List  6. 返回值为String(数据) 在SpringMVC框架中,我们最常见的就是@Controller注解,可以说是只要见到了@Controller注解,就可以说明当前的项目使用了SpringMVC框架。那么在使

    2024年02月19日
    浏览(42)
  • Mybatis中的关系映射

    目录 前言 1.一对一的映射关系 1.1 创建模型类和Vo类  1.2 配置当前模型类的mapper.xml 1.3 开始测试 2.一对多的映射关系 2.1 创建模型类和Vo类 2.2 配置当前模型类的mapper.xml 2.3 开始测试 3.多对多的映射关系 总结  注意点:  一对一映射(One-to-One Mapping) : 一对一关系指的是两个

    2024年02月09日
    浏览(39)
  • MyBatis关联关系映射详解

    目录 前言 一、 什么是关联关系映射? 二、MyBatis的关系映射方式 1.基于XML配置的关系映射 2.基于注解的关系映射 三、如何使用MyBatis进行关系映射? 四、关于关系映射的一些建议 五、关联关系映射 1.一对一关联关系映射 嵌套查询 嵌套结果映射 2.一对多关联关系映射 嵌套查

    2024年02月09日
    浏览(64)
  • Elasticsearch(2)——映射关系

    1 什么是映射 映射(mapping)就像数据库中的 Schema ,描述了文档可能具有的字段或属性、每个字段的 数据类型,比如 Text,Keyword,Integer 或 Date ,以及 Lucene 是如何索引和存储这些字 段的。 Elasticsearch 支持如下简单字段类型: (1) 字符串: text,keyword (2)整数:byte,sho

    2024年02月12日
    浏览(36)
  • ES(3)映射关系

    创建 mapping 映射类似于我们创建表结构,规定字段什么类型,多长等基本信息。 先创建 索引 PUT http://127.0.0.1:9200/user 然后创建映射关系 PUT http://127.0.0.1:9200/user/_mapping POST http://127.0.0.1:9200/user/_doc/1001 GET http://127.0.0.1:9200/user/_search 会发现没有查到数据,为什么没有分词呢?因为

    2024年02月15日
    浏览(35)
  • 【SpringMVC篇】探索请求映射路径,Get请求与Post请求

    🎊专栏【SpringMVC】 🍔喜欢的诗句:天行健,君子以自强不息。 🎆音乐分享【如愿】 🎄欢迎并且感谢大家指出小吉的问题🥰 请求映射是SpringMVC框架进行请求调度的重要基础。通过请求映射,SpringMVC可以将不同的请求映射到指定的控制器进行处理。所以学习使用请求映射是精

    2024年02月08日
    浏览(80)
  • 什么是ORM(对象关系映射)?

    ORM(对象关系映射)是一种编程技术,用于在关系型数据库和面向对象编程语言之间建立映射关系。它的目标是通过自动化和简化数据访问层的开发,将数据库表和记录映射到面向对象编程语言中的对象和类。 ORM提供了一种将数据库中的数据转换为编程语言中的对象的机制,

    2024年02月12日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包