精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读
Spring 版本:5.2.4.RELEASE
该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》
HandlerMapping 组件
HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors)
handler处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的@RequestMapping注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法HandlerInterceptor拦截器对处理请求进行增强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理
由于 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:
- 《HandlerMapping 组件(一)之 AbstractHandlerMapping》
- 《HandlerMapping 组件(二)之 HandlerInterceptor 拦截器》
- 《HandlerMapping 组件(三)之 AbstractHandlerMethodMapping》
- 《HandlerMapping 组件(四)之 AbstractUrlHandlerMapping》
HandlerMapping 组件(四)之 AbstractUrlHandlerMapping
先来回顾一下HandlerMapping 接口体系的结构:

在《HandlerMapping 组件(一)之 AbstractHandlerMapping》文档中已经分析了 HandlerMapping 组件的 AbstractHandlerMapping 抽象类基类
那么本文就接着来分析图中黄色框部分的 AbstractUrlHandlerMapping 系,基于 URL 进行匹配。例如 《基于 XML 配置的 Spring MVC 简单的 HelloWorld 实例应用》 ,当然,目前这种方式已经基本不用了,被 @RequestMapping 等注解的方式所取代。不过,Spring MVC 内置的一些路径匹配,还是使用这种方式。
因为 AbstractUrlHandlerMapping 在实际开发基本不会涉及到,所以本文选读,可以直接查看总结部分
一共有五个子类,分成两条线:
- AbstractUrlHandlerMapping <= SimpleUrlHandlerMapping <= WebSocketHandlerMapping
- AbstractUrlHandlerMapping <= AbstractDetectingUrlHandlerMapping <= BeanNameUrlHandlerMapping
其中,WebSocketHandlerMapping 是 spring-websocket 项目中的类,本文会无视它
所以,本文按照 AbstractUrlHandlerMapping、SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping 顺序进行分析
回顾
先来回顾一下在 DispatcherServlet 中处理请求的过程中通过 HandlerMapping 组件,获取到 HandlerExecutionChain 处理器执行链的方法,是通过AbstractHandlerMapping 的 getHandler 方法来获取的,如下:
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// <1> 获得处理器(HandlerMethod 或者 HandlerExecutionChain),该方法是抽象方法,由子类实现
Object handler = getHandlerInternal(request);
// <2> 获得不到,则使用默认处理器
// <3> 还是获得不到,则返回 null
// <4> 如果找到的处理器是 String 类型,则从 Spring 容器中找到对应的 Bean 作为处理器
// <5> 创建 HandlerExecutionChain 对象(包含处理器和拦截器)
// ... 省略相关代码
return executionChain;
}
在 AbstractHandlerMapping 获取 HandlerExecutionChain 处理器执行链的方法中,需要先调用 getHandlerInternal(HttpServletRequest request) 抽象方法,获取请求对应的处理器,该方法由子类去实现,也就上图中黄色框和红色框两类子类,本文分析黄色框部分内容
AbstractUrlHandlerMapping
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping,实现 MatchableHandlerMapping 接口,继承 AbstractHandlerMapping 抽象类,以 URL 作为 Handler 处理器 的 HandlerMapping 抽象类,提供 Handler 的获取、注册等等通用的骨架方法。
构造方法
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
/**
* 根路径("/")的处理器
*/
@Nullable
private Object rootHandler;
/**
* 使用后置的 / 匹配
*/
private boolean useTrailingSlashMatch = false;
/**
* 是否延迟加载处理器,默认关闭
*/
private boolean lazyInitHandlers = false;
/**
* 路径和处理器的映射
*
* KEY:路径 {@link #lookupHandler(String, HttpServletRequest)}
*/
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
}
registerHandler
registerHandler(String[] urlPaths, String beanName) 方法,注册多个 URL 的处理器,方法如下:
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
// <1> 如果非延迟加载,并且 handler 为 String 类型,并且还是单例,则去获取 String 对应的 Bean 对象
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
// <2> 获得 urlPath 对应的处理器
Object mappedHandler = this.handlerMap.get(urlPath);
// <3> 检验 mappedHandler 是否已存在,如果已存在,并且不是当前 resolvedHandler 对象,则抛出异常
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
// <4.1> 如果是 / 根路径,则设置为 rootHandler
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
// <4.2> 如果是 /* 路径,则设置为默认处理器
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
// <4.3> 添加到 handlerMap 中
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
遍历 URL,依次注册处理器
- 如果非延迟加载,并且
handler为 String 类型,并且还是单例,则去获取 String 对应的 Bean 对象,resolvedHandler - 从
handlerMap中获得urlPath对应的处理器 - 如果该路径已存在对应的处理器,但是不是当前
resolvedHandler对象,则抛出异常 - 否则,该路径不存在对应的处理器,则将当前
resolvedHandler处理器保存- 如果是
/根路径,则设置resolvedHandler为rootHandler - 否则,如果是
/*路径,则设置为默认处理器defaultHandler(在父类中) - 否则,添加到
handlerMap中
- 如果是
getHandlerInternal
实现父类的 getHandlerInternal(HttpServletRequest request) 方法,获得处理器,方法如下:
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// <1> 获得请求的路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// <2> 获得处理器
Object handler = lookupHandler(lookupPath, request);
// <3> 如果找不到处理器,则使用 rootHandler 或 defaultHandler 处理器
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
// <3.1> 如果是根路径,则使用 rootHandler 处理器
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
// <3.2> 使用默认处理器
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
// <3.3> 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
// <3.4> 空方法,校验处理器。目前暂无子类实现该方法
validateHandler(rawHandler, request);
// <3.5> 创建处理器(HandlerExecutionChain 对象)
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
获得请求路径
调用
lookupHandler(String urlPath, HttpServletRequest request)方法,获得处理器,详情见下文如果找不到处理器,则使用
rootHandler或defaultHandler处理器- 如果是
/根路径,则使用rootHandler处理器 - 否则,使用
defaultHandler默认处理器 - 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
- 调用
validateHandler(Object handler, HttpServletRequest request),对处理器进行校验,空方法,暂无子类实现该方法 - 调用
buildPathExposingHandler方法,创建 HandlerExecutionChain 处理器执行链,赋值给handler处理器,详情见下文
- 如果是
返回请求对应的
handler处理器
所以说这里但会的处理器对象可能是一个 HandlerExecutionChain 对象,用途目前不清楚
精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping的更多相关文章
- 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(三)之 AbstractHandlerMethodMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerExceptionResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - MultipartResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
随机推荐
- 死磕以太坊源码分析之p2p节点发现
死磕以太坊源码分析之p2p节点发现 在阅读节点发现源码之前必须要理解kadmilia算法,可以参考:KAD算法详解. 节点发现概述 节点发现,使本地节点得知其他节点的信息,进而加入到p2p网络中. 以 ...
- 【Azure微服务 Service Fabric 】Service Fabric中应用开启外部访问端口及微服务之间通过反向代理端口访问问题
问题描述 1) 当成功的在Service Fabric集群中部署了应用后,如何来访问呢?如果是一个Web服务,它的URL又是什么呢? 2) 当Service Fabric集群中,服务之间如需要相互访问 ...
- Vue—新版本router-view 与 keep-alive 的互动
1. <keep-alive> 直接嵌套到 <router-view> 上会失效,正确写法: <router-view #="{ Component }&quo ...
- 程序媛数据报告:近三年增长至70%,平均月薪1.54W,女性程序媛并不是特殊物种
- 数论之prufer序列
定义 \(Prufer\) 数列是无根树的一种数列. 在组合数学中,\(Prufer\) 数列由有一个对于顶点标过号的树转化来的数列,点数为 \(n\) 的树转化来的 \(Prufer\) 数列长度为 ...
- JDK7HashMap
JDK7HashMap 成员变量 HashMap中定义了非常多的成员变量以及常量,各成员变量含义具体如下: //默认初始化长度-16 static final int DEFAULT_INITIAL_ ...
- JZOJ 2020.10.6 提高B组反思
JZOJ 2020.10.6 提高B组反思 T1 NYG的动态数点 最简单的一题 很容易想到\(O(n)\)的做法 枚举最小的那个数,即\(a_k\) 然后向左和向右扩展 然后可以直接从右端点+1继续 ...
- 痞子衡嵌入式:深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程. 从外部串行NOR Flash启动问题是i.MXRT系列开发最 ...
- 【原创】Linux虚拟化KVM-Qemu分析(七)之timer虚拟化
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...
- Java解决大文件读取的内存问题以及文件流的比较
Java解决大文件读取的内存问题以及文件流的比较 传统方式 读取文件的方式一般是是从内存中读取,官方提供了几种方式,如BufferedReader, 以及InputStream 系列的,也有封装好的如 ...