Spring-20 SpringMVC 请求映射
Spring 源码系列文章会遵循由浅入深,由易到难,由宏观到微观的原则,目标是尽量降低学习难度,而不是一上来就迷失在源码当中. 文章会从一个场景作为出发点,针对性的目的性极强的针对该场景对 Spring 的实现原理,源码进行探究学习。该系列文章会让你收获什么? 从对 Spring 的使用者成为 Spring 专家。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。
本章内容会去详细介绍 Spring MVC
中的 HandlerMapping
组件,也就是核心功能中的将一次 HTTP
请求路由到一个处理器上的过程详解。其实说是详解但是内容并不会很多,不会很细致的去讲解源码,感觉没那个必要(其实是我懒)。会把抽象整理好的,便于理解的内容呈现给读者,给读者一个高度上层视角,而不是陷入到源码的迷宫中。
所谓的处理器映射器
, 请求映射
或者 请求路由
巴拉巴拉一堆名词都是指上图中的 Request Mapping
这个过程 , 完整的表述就是 Http Request Mapping To Java Method
. 对应的在 Spring MVC
中实现这个功能的就是 HandlerMapping
所代表的一系列实现类。面向接口编程,体现了框架的可扩展性和代码的稳定性,很优雅。
面向接口编程的两个重要目标就是为了提高扩展性和代码的稳定性(符合开闭原则的代码就是稳定的代码)。比如在
DispatcherServlet
核心代码不做任何的变更情况下,我可以灵活的插拔某个HandlerMapping
的实现,这样做我就可以改变框架的行为,但是却不用对DispatcherServlet
的代码做任何一行的更改,这样一来DispatcherServlet
就是一个经过反复测试稳定的类。而我改变框架行为的动作只会限定在很小的一个范围内,比如我自定义了一个HandlerMapping
的实现,这样做隔离了变化也就隔离了风险。因为如果我总对某一个类进行修改,那么这个类始终都是不稳定的,毕竟没有人可以承诺他经过修改的代码 100% 没有问题。
HandlerMapping 组件
映射过程的核心方法是 AbstractHandlerMapping#getHandlerInternal
衍生出了一些扩展实现分别提供不同的映射方式,具体的看图这里就不多说了。
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
这个方法的返回值是
Object
这就说明对于处理器类的扩展来说是非常的灵活的,当然也需要其他组件一起协同配合支持。
我们平时用的最频繁的是 RequestMappingInfoHandlerMapping
这个实现就是匹配基于 @RequestMapping
注解的处理器。它的处理映射的核心方法是 RequestMappingInfoHandlerMapping#getMatchingMapping
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
return info.getMatchingCondition(request);
}
再看下 RequestMappingInfo#getMatchingCondition
这个方法,是不是很简单瞬间就懂了。
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
if (methods == null) {
return null;
}
ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
if (params == null) {
return null;
}
HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
if (headers == null) {
return null;
}
ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
if (consumes == null) {
return null;
}
ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
if (produces == null) {
return null;
}
PathPatternsRequestCondition pathPatterns = null;
if (this.pathPatternsCondition != null) {
pathPatterns = this.pathPatternsCondition.getMatchingCondition(request);
if (pathPatterns == null) {
return null;
}
}
PatternsRequestCondition patterns = null;
if (this.patternsCondition != null) {
patterns = this.patternsCondition.getMatchingCondition(request);
if (patterns == null) {
return null;
}
}
RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
if (custom == null) {
return null;
}
return new RequestMappingInfo(this.name, pathPatterns, patterns,
methods, params, headers, consumes, produces, custom, this.options);
}
另外再说一下源码没必要每一行都去看,浪费时间性价比不高,你只需要掌握看源码的能力,还有就是对某个技术或框架有更高视角的了解,大致知道实现原理和部分细节,目的就是为了能把这个技术用的更好,或者更好的解决问题。
匹配到处理器之后会把处理器和拦截器组装成一个调用链对象。
下一个核心步骤就是如何把 HTTP Message
转换成处理器需要的 Java Object
了,这个下一篇内容再讲。
DevX
会持续分享有趣的技术和见闻,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。