该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读

Spring 版本:5.2.4.RELEASE

该系列其他文档请查看:《精尽 Spring MVC 源码分析 - 文章导读》

HandlerMapping 组件

HandlerMapping 组件,请求的处理器匹配器,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器们(interceptors

  • handler 处理器是 Object 类型,可以将其理解成 HandlerMethod 对象(例如我们使用最多的 @RequestMapping 注解所标注的方法会解析成该对象),包含了方法的所有信息,通过该对象能够执行该方法

  • HandlerInterceptor 拦截器对处理请求进行增强处理,可用于在执行方法前、成功执行方法后、处理完成后进行一些逻辑处理

由于 HandlerMapping 组件涉及到的内容比较多,考虑到内容的排版,所以将这部分内容拆分成了四个模块,依次进行分析:

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,依次注册处理器

  1. 如果非延迟加载,并且 handler 为 String 类型,并且还是单例,则去获取 String 对应的 Bean 对象,resolvedHandler
  2. handlerMap中获得 urlPath 对应的处理器
  3. 如果该路径已存在对应的处理器,但是不是当前 resolvedHandler 对象,则抛出异常
  4. 否则,该路径不存在对应的处理器,则将当前 resolvedHandler 处理器保存
    1. 如果是 / 根路径,则设置 resolvedHandlerrootHandler
    2. 否则,如果是 /* 路径,则设置为默认处理器 defaultHandler(在父类中)
    3. 否则,添加到 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;
}
  1. 获得请求路径

  2. 调用 lookupHandler(String urlPath, HttpServletRequest request) 方法,获得处理器,详情见下文

  3. 如果找不到处理器,则使用 rootHandlerdefaultHandler 处理器

    1. 如果是/根路径,则使用 rootHandler 处理器
    2. 否则,使用 defaultHandler 默认处理器
    3. 如果找到的处理器是 String 类型,则从容器中找到该 beanName 对应的 Bean 作为处理器
    4. 调用validateHandler(Object handler, HttpServletRequest request),对处理器进行校验,空方法,暂无子类实现该方法
    5. 调用 buildPathExposingHandler方法,创建 HandlerExecutionChain 处理器执行链,赋值给handler处理器,详情见下文
  4. 返回请求对应的handler处理器

所以说这里但会的处理器对象可能是一个 HandlerExecutionChain 对象,用途目前不清楚

最新文章

  1. HTML基础
  2. VS2015下如何用编译、调试程序。
  3. [Unity3d]向量的过度方法以及拖尾效果
  4. asp.net core实现SHA1withRSA
  5. Trace-跟踪高消耗的语句需添加哪些事件
  6. Python 3学习笔记2
  7. Java NIO流 -- 缓冲区(Buffer,ByteBuffer)
  8. poj 1759 Garland (二分搜索之其他)
  9. Sql存储过程解密方法
  10. Turn the corner--hdu2438(3分法)
  11. MySQL导出以H开头的表
  12. async和enterproxy控制并发数量
  13. 虚拟机安装Ubuntu14.04打开FireFox提示Server not found
  14. 关于C#中程序功能实现,对代码选择的思考
  15. Bugku-CTF之never give up
  16. python学习Day12 函数的默认值、三元表达式、函数对象(函数名)的应用场景、名称空间与作用域
  17. python_flask 基础巩固 (DEBUG模式)
  18. js判断一个图片是否已经存在于缓存
  19. rrd文件及rrd文件与实际数据的对比研究。
  20. Linux性能测试分析命令_sar

热门文章

  1. MySql的远程登录问题
  2. 写的太细了!Spring MVC拦截器的应用,建议收藏再看!
  3. 不是吧!做了两年java还没弄懂JVM堆?进来看看你就明白了
  4. 听法国设计师大卫&#183;维森特讲述他与CorelDRAW的渊源
  5. 详解FL Studio压缩器——Fruity Limiter(上)
  6. CleanMyMac X“断网激活”真的能激活软件吗?
  7. 关于ABBYY的常见问题与解答
  8. 【PUPPETEER】初探之原生frame切换(四)
  9. SkyWalking —— 分布式应用监控与链路追踪
  10. NetSuite实现pdf打印中的条形码的功能