二、HandlerAdapter

根据 Handler 来找到支持它的 HandlerAdapter,通过 HandlerAdapter 执行这个 Handler 得到 ModelAndView 对象。HandlerAdapter 接口中的方法如下:

  • boolean supports(Object handler); // 当前 HandlerAdapter 是否支持这个 Handler
  • ModelAndView handle(HttpServletRequest req, HttpServletResponse res, Object handler); // 利用 Handler 处理请求
  • long getLastModified(HttpServletRequest request, Object handler);

1 RequestMappingHandlerAdapter

从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HadnlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HadnlerMethod 对象。

RequestMappingHandlerAdapter 的 handle() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,源码如下所示

@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal() 方法是由 RequestMappingHandlerAdapter 自己来实现的,源码如下所示

@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 是否通过 @SessionAttributes 注释声明了 session 属性。
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
} else {
checkAndPrepare(request, response, true);
}
// 是否需要在 synchronize 块中执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 执行 HandlerMethod
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
// 执行 HandlerMethod,得到 ModelAndView
return invokeHandleMethod(request, response, handlerMethod);
}

继续再来看一下如何得到 ModelAndView,invokeHandlerMethod() 方法如下

private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 数据绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 绑定参数,执行方法
ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
// 创建模型和视图容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 设置FlasgMap中的值
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 初始化模型
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout); final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
}
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}

2 HttpRequestHandlerAdapter

HttpRequestHandlerAdapter 可以执行 HttpRequestHandler 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}

3 SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter 可以执行 Controller 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}

4 SimpleServletHandlerAdapter

SimpleServletHandlerAdapter 可以执行 Servlet 类型的 Handler,源码如下

@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((Servlet) handler).service(request, response);
return null;
}

三、HandlerExceptionResolver

负责处理异常的类,负责根据异常来设置 ModelAndView,然后交由 render 渲染界面。HandlerExecptionResolver 接口中只有一个方法,如下:

  • ModelAndView resolveException(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex);

不同的映射处理器(HandlerMapping) 映射出来的 handler 对象是不一样的,AbstractUrlHandlerMapping 映射器映射出来的是 handlerController 对象,AbstractHandlerMethodMapping 映射器映射出来的 handlerHandlerMethod 对象。由此我们猜想映射的处理器也应该有很多种,不同的映射由不同的适配器来负责解析。

1 HandlerAdapter 基础了解

1.1 首先我们来看下适配器 HandlerAdapter 接口内部的方法

1.2 再来看下适配器 HandlerAdapter 接口的实现链

1.3 适配器们的加载

首先我们来看下源码,容器初始化的时候会将注入到容器的适配器们加进缓存。

首先扫描注入容器的适配器,这里需要注意下 <mvc:annotation-driven /> 会帮我们注入 RequestMappingHandlerAdapterHttpRequestHandlerAdapterSimpleControllerHandlerAdapter 这三个配置器,我们需要注意下不要手动重复注入。

当我们没有写 <mvc:annotation-driven /> 标签帮我们注入,也没有手动注入,从上面源码最后一步可以看到,容器在初始化的时候检测到会自动帮我们注入 RequestMappingHandlerAdapterHttpRequestHandlerAdapterSimpleControllerHandlerAdapter 这三个配置器。

2 HttpRequestHandlerAdapter

可以执行 HttpRequestHandler 类型的 handler,源码如下

3 SimpleServletHandlerAdapter

可以执行 Servlet 类型的 handler,源码如下

4 SimpleControllerHandlerAdapter

可以执行 Controller 类型的 handler,源码如下

在前面 SpringMVC工作原理之处理映射[HandlerMapping] 笔记中使用 AbstractUrlHandlerMapping 的子类映射器最终返回的 handlerController 类型,当时定义的视图控制器都继承自 AbstractController,视图必须实现 handleRequestInternal(...) 方法。

AbstractController 内部源码如下

从源码中可以看出 Controller 对象调用 handlerRequest(..) 方法最终经过处理后还是调用 handleRequestInternal (..) 方法。

5. AbstractHandlerMethodAdapter

在这里 AbstractHandlerMethodAdapter 只有一个实现子类就是 RequestMappingHandlerAdapter,首先我们来看下其内部是怎么来实现判断 supports(..) 是否支持该 handler 和接下来的 handler 解析方法 handle(..) 的,源码如下

 

6. 总结

关于适配器的分类总结及适配,本篇笔记就记录到这里。前面讲到的几种适配器执行对应的 handler 都很简单,主要是 RequestMappingHandlerAdapter 适配器,该适配从上面可以看出

biancheng-Spring MVC-HandlerAdapter的更多相关文章

  1. Spring MVC中的HandlerMapping与HandlerAdapter

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  2. spring mvc 关键接口 HandlerMapping HandlerAdapter

    HandlerMapping Spring mvc 使用HandlerMapping来找到并保存url请求和处理函数间的mapping关系.   以DefaultAnnotationHandlerMa ...

  3. Spring MVC 梳理 - handlerMapping和handlerAdapter分析

    参考图片 综上所述我们来猜测一下spring mvc 中根据URL找到处理器Controller中相应方法的流程 ①:获取Request的URL ②:从UrlLookup这个map中找到相应的requ ...

  4. Spring mvc之源码 handlerMapping和handlerAdapter分析

    Spring mvc之源码 handlerMapping和handlerAdapter分析 本篇并不是具体分析Spring mvc,所以好多细节都是一笔带过,主要是带大家梳理一下整个Spring mv ...

  5. Spring MVC源码分析(三):SpringMVC的HandlerMapping和HandlerAdapter的体系结构设计与实现

    概述在我的上一篇文章:Spring源码分析(三):DispatcherServlet的设计与实现中提到,DispatcherServlet在接收到客户端请求时,会遍历DispatcherServlet ...

  6. spring mvc(5) HandlerAdapter

    前面我们讲到了通过HandlerMapping可以获得不同类型的处理器,可以是Controller.HttpRequestHandler.Servlet.HandlerMethod甚至是我们自定义的处 ...

  7. 精尽Spring MVC源码分析 - HandlerAdapter 组件(一)之 HandlerAdapter

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

  8. 精尽Spring MVC源码分析 - HandlerAdapter 组件(二)之 ServletInvocableHandlerMethod

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

  9. 精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver

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

  10. 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

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

随机推荐

  1. OpenAI使用AI编程给出了数数问题的解决方案 —— 如何解决ChatGPT不会数数的问题

    总所周知的一个问题,那就是ChatGPT不会数数,不过今天突然发现OpenAI给出了一个神奇的解决方法,那就是AI编程. 问题案例如下: The text provided will be analy ...

  2. CCF网站提供的资源

    1. 电子刊物,如<CCF通讯> 实时更新:http://www.ccf.org.cn/dl/publications/ 过往期刊:http://history.ccf.org.cn/si ...

  3. Codeforces 4 A-D

    题面 A B C D 难度:红 橙 橙 黄 题解 A 题目大意: 判断一个正整数 \(w\) 能否表示成两个正偶数之和. 解题思路: 考虑分类讨论 \(w\). 对于 \(1\) 和 \(2\),显然 ...

  4. 共享存储ISCSI

    建立共享iscsi磁盘组 资源环境 服务端:192.168.2.131 客户端:192.168.2.[110,169] 服务端磁盘: [root@centos ~]# lsblk NAME MAJ:M ...

  5. 21.Kubernetes配置默认存储类

    Kubernetes配置默认存储类 前言 今天在配置Kubesphere的时候,出现了下面的错误 经过排查,发现是这个原因 我通过下面命令,查看Kubernetes集群中的默认存储类 kubectl ...

  6. i-MES生产制造管理系统-可视化看板

    可视化看板最主要的目的是为了将生产状况透明化,让大家能够快速了解当前的生产状况以及进度,通过大数据汇总分析,为管理层做决策提供数据支撑,看板数据必须达到以下基本要求: 数据准确--真实反映生产情况 数 ...

  7. Centos7 安装python3与python2.7 共存

    前言 在centos7服务器上,linux系统默认安装有python2.7,这是系统服务等会依赖到的,所以系统的python2.7是不可以卸载的,避免系统出现问题.那么问题就来了,我们现在使用的pyt ...

  8. 2019-2020 ACM-ICPC Brazil Subregional Programming Contest

    D. Denouncing Mafia 给定一颗树,然后给定\(k\)个起点,对于每个起点来说,从该点到根节点的一条链都会被染色,求最多有几个点会被染色 \(3 \leq n \leq 1e5, 1 ...

  9. Javascript 标签的属性

    1.为HTML标签设置和添加属性 setAttribute() setAttribute()方法可以给HTML标签设置/添加属性(原生的属性或者自定义的属性都可以)添加的属性会存储在标签中 <! ...

  10. HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件

    HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件 原文地址:https://www.stevejgordon.co.uk/http ...