HandlerMapping之RequestMappingHandlerMapping作用详解

前言

上回我们知道HandlerMapping是用来寻找Handler的,并不与Handler的类型或者实现绑定,而是根据需要定义的。

那么为什么要单独给@RequestMapping实现一个HandlerMapping?这次咱们就来专门看看这个RequestMappingHandlerMapping。

RequestMappingHandlerMapping

名字来源

因为RequestMappingHandlerMapping是专门为@RequestMapping而生的,因此他的名字是这样来的:@RequestMapping的HandlerMapping了。

为什么不叫MethodHandlerMapping呢?

主要还是Handler是一个逻辑概念,MethodHandler了只是对目标方法进行了封装,并不是真正处理请求的。

真正处理请求的是我们@RequestMapping的方法。取个名字都给你讲道理。

@RequestMapping

在解答文章开头的问题前,我们先看看@RequestMapping

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
	/**
	 * 处理器的名字,支持类级别和方法级别,多个路径用#分割
	 */
	String name() default "";
	/**
	 * 匹配请求路径
	 */
	@AliasFor("path")
	String[] value() default {};
	/**
	 * 匹配请求路径
	 */
	@AliasFor("value")
	String[] path() default {};
	/**
	 * Http请求方法:可选GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE
	 */
	RequestMethod[] method() default {};
	/**
	 * 匹配指定的地址栏参数
	 */
	String[] params() default {};
	/**
	 * 匹配特定的header
	 */
	String[] headers() default {};
	/**
	 * 匹配特定的Content-Type
	 */
	String[] consumes() default {};
	/**
	 * 匹配特定的Accept
	 */
	String[] produces() default {};
}

发现了吗,各位?他除了能根据URI匹配,还能根据请求头、请求方法、甚至是请求参数来匹配!

上回说的基于URL的两种HandlerMapping都不能满足他,因此必须推出一个更加强大、可扩展性更强的HandlerMapping——RequestMappingHandlerMapping

那么问题来了:

  • @RequestMapping是在什么如何被解析的呢?
    • 我们很容易想到的就是,遍历容器中所有的对象,检查是否存在@Controller注解。存在,那就是控制器。然后接着遍历所有声明的public方法,检查是否存在@RequestMapping方法。这样,我们就找到了处理器方法。
  • @RequestMapping是在什么时候被解析的呢?
    • 本着“谁使用,谁解析”的原则,他自然是被RequestMappingHandlerMapping解析的。而又因为@RequestMapping的寻找可太费功夫,不可能在提供映射服务时再来解析,只能是初始化时进行解析。因此实现InitializingBean进行初始化,是个选择。

没错,实际上,SpringMVC跟你想的一样。在InitializingBean的afterPropertiesSet方法中,完成了以3下件事:

  1. 寻找@Controller的bean,并找到所有的@RequestMapping方法
  2. 解析@RequestMapping封装成RequestMappingInfo
  3. 将以上解析到的信息进行注册。

信息包括:

信息描述
@Controller/@RequestMapping对象反射调用目标方法时,需要的target对象
RequestMappingInfo由@RequestMapping解析而来
@RequestMapping的方法注册时,注册器会将Method与handler对象一起封装成HandlerMethod进行注册。便于后面适配器调用。

@RequestMapping的注册

前面的解析@RequestMapping到RequestMappingInfo,可以省略,比较简单。但是@RequestMapping的注册没办法省略。因为如果搞不清楚他是怎么注册的,也就没办法理解他是怎么寻找目标处理器的。

为了支撑@RequestMapping多样化的匹配条件,不能再像前面两款HanderMapping一样,简单粗暴的使用Map了。在 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 的内部定义了内部类,专门用来做注册中心,管理映射关系。

/**
	 * Mapping注册中心,可以理解为办事处
	 */
	class MappingRegistry {
		/**
		 * T是匹配条件的对象。MappingRegistration是注册的信息,可以理解为你要做事情。
		 * 对于RequestMappingHandlerMapping,T就是RequestMappingInfo
		 * MappingRegistration包括信息:RequestMappingInfo、HandlerMethod、directPaths、mappingName、corsConfig
		 */
		private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
		/**
		 * Map<path, RequestMappingInfo>
		 */
		private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
		/**
		 * Map<mappingName, List<HandlerMethod>>
		 */
		private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
	}

 

从他的属性,我们可以分析得到如下信息:

  1. 直接通过请求路径来查找处理器时,需要经过pathLookup中转registry,最后拿到MappingRegistration才到达HandlerMethod.
  2. 直接通过mappingName则可以直接找到HandlerMethod. 不过这个是Spring为了支持 <%@ taglib uri="//www.springframework.org/tags" prefix="s" %> 而提供的。跟我们平时使用没多大关系。

但是各位观众老爷,pathLookup找到的是一个,而mappingName能找到多个,这是咋回事?误会啊,pathLookup的value可不是简单的一个元素,而是多个!他是MultiValueMap,不是我们经常看到的地摊货HashMap。他可以一个key对应多个value。

但是为什么会有多个呢?或者说为什么需要保存多个呢? 因为一个Handler可以处理多个请求,如果由多个Handler都能处理某一个请求的时候怎么办呢?况且SpringMVC还支持通配符匹配。umm…这一幕有点似曾相识,我们的nginx做路由转发的时候,不是也有类似的问题吗?这意味着在查找Handler的时候,我们还需要找到最佳的匹配。例如,/*相较于/hello,那肯定是/hello更精确,更合适啦。你看,多严谨!

总结

由于@RequestMapping支持灵活的请求匹配条件,而不只是简单的路径,只能开发出RequestMappingHandlerMapping进行支持。

RequestMappingHandlerMapping是通过InitializingBean进行初始化的,在这里完成@RequestMappingHandlerMapping的扫描和解析,以及注册。

HandlerMethod是在注册时进行封装的。获取Handler时,拿到的Handler就是HandlerMethod。

后面的适配器适配的,也是他。

RequestMappingHandlerMapping使用了InitializingBean做初始化,但是当我们自己在做初始化的时候,尤其是使用多种初始化方式的时候,应当要注意Spring的调用顺序,否则有可能发生NPE,或者获取不到目标属性的情况。

例如:同时在ApplicationContextAware、InitializingBean、@PostConstruct进行初始化。 为此,给大家找了官方的bean的生命周期

 

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
RequestMappingHandlerMapping是Spring框架中的一个类,它主要用于将请求映射到对应的Handler(controller)上。在处理请求之前,需要确定由哪个方法来处理该请求,而这个功能就是由RequestMappingHandlerMapping来完成的。它的初始化过程中,会调用handlerMappings来获取HandlerMapping。 在具体的实现中,RequestMappingHandlerMapping继承了AbstractHandlerMapping类,并在其中的getHandler方法中获取请求对应的HandlerExecutionChain。 所以,RequestMappingHandlerMapping通过调用父类的方法,间接地获取handlermapping。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [请求处理过程1-获取HandlerMapping](https://blog.csdn.net/weixin_46666822/article/details/124749832)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [HandlerMappingRequestMappingHandlerMapping获取Handler](https://blog.csdn.net/hjtlovelife/article/details/104705479)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ok060

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值