Spring MVC:DispatcherServlet

在 Spring MVC 中,有一个类是必定要去了解和熟悉的,这个类就是 DispatcherServlet,该类负责了 MVC 中最主要的内容——请求转发处理。 在本文中,我们会在源码级别讲述 DispatcherServlet 的运行机制和高度灵活的扩展性。

DispatcherServlet 的处理过程

DispatcherServlet 的处理过程更像是一个循环执行接口程序的过程,只是有几个相对独立的部分。如果使用 IDE 进行断点你会发现它的流程如下:
  1. doService
    1. 解析请求属性
  2. doDispatch
    1. getHandler( 请求到 Handler 对象映射 )
    2. getHanderAdapter
    3. preHandle
    4. handle
    5. postHandle
    6. processDispatchResult
      1. 完成异常解析
      2. render
        1. 区域处理
        2. 视图名解析
        3. 视图呈现
    7. afterCompletion

DispatcherServlet 的功能

DispatcherServlet 运行过程有以下几个大的功能板块:
  • 基于 JavaBeans 的方式进行配置
  • 控制 Handler 对象路由:HandlerMapping
  • Handler 适配处理:HandlerAdapter
  • 异常处理策略
  • 视图解析策略
  • 请求视图名转译
  • 复合表单内容处理
  • 区域解析
  • 主题解析
Java Beans 方式配置是 Java EE 中的一个规范,其定义 Java Beans 支持属性,事件和方法。如果你对 Java Beans 还不够熟悉可以查看 Oracle 对 Java Beans 是怎样编写的来了解具体的信息
HandlerMapping 负责了请求的路由,也就是说在接收到请求后,请求会经过它的处理最后返回 Handler 对象(可以是任意的类型)。其运行机制由两个组件组成:HandlerInterceptor 和 HandlerExecutionChain。HandlerInterceptor 有三个主要方法:preHandle(),postHandle(),afterCompletion()。
  • preHandle() 方法在 HandlerAdapter 发起调用 Handler 时触发。如果返回 true 表示会处理下一个 Interceptor,否则会认为以及处理完请求。
  • postHandle() 方法在 HandlerAdapter 调用 Handler 并且在 呈现视图 之前调用
  • afterCompletion() 仅在 preHandle() 方法返回 true 并且呈现视图之后执行
HandlerExcutionChain 类真正地组织 HandlerInterceptor 和 Handler 对象。并且真正在调用preHandle(),postHandle(),afterCompletion()。最后这个对象在 HandlerMapping 中 getHandler() 进行返回。
HandlerAdapter (适配器模式)负责完成 Handler 的实际处理。在 HandlerMapping 的处理结果的基础上,HandlerAdapter 表达了如何运作 Handler 对象,如 SimpleControllerHandlerAdapter 是针对 Controller 接口调用 Handler 对象的 handleRequest() 方法完成处理的。
异常处理由 HandlerExceptionResolver 负责,在流程 3-7 步中,如果抛出了异常会经由 processDispatchResult() 进行判断最后交由 processHandlerException() 进行检查异常解析结果,循环判断(最后一个
HandlerExceptionResolver 为第一个判断)有一个可以处理(判断 ModelAndView 返回值不为空)那么就会认为处理完成并把 ModelAndVIew 进行返回,间接使用 视图解析 的执行路径 。
视图解析,由 ViewResolver 负责,ViewResolver 会通过 viewName 获取到相应的 View 对象最后完成 ModelAndView 的呈现工作。与上面的做法先类似,都是使用循环的方式进行解析,一旦解析成功直接返回。 processDispatchResult() 负责调用 render() 方法呈现相应的页面结果。在 render() 方法中,地区解析是在这里完成的,它可以使用 Accept-Language 请求头来给 response 对象设置区域(Content-Language)响应头。最后通过 resolveViewName 获取到交由 View 对象进行实际的呈现工作。
请求视图名转译,通过请求获取相应的视图名,最后经过 ViewResolver 解析出相应的 View 对象,默认的转译类(DefaultRequestToViewNameTranslator)是把请求路径和资源名返回,如 http://localhost/a/b/c.html 将会返回 a/b/c

DispatchServlet 应用

  1. 自定义 HandlerIntercepter ,可以完成与 Handler 进行绑定的拦截处理,而不需要 AOP 编程
  2. 自定义 HandlerAdapter,可以实现自定义的 Handler 如实现一套不同的控制器处理逻辑
  3. 自定义 ViewResovler 视图解析,实现定制的 视图 呈现方式
  4. 自定义 HandlerExceptionResolver 异常处理,

毕业回顾

回想已经经过的时间,总是让人着迷和反思。每个选择的节点依然清晰无比,却又那么的扎心。每个人或许都觉得之前能够选择得更好,不过我至少现在的我看来这一切都是固定的。现在的你觉得之前可以做得更好是因为你得到了成长,那么接下来会是什么等待自己,重蹈覆辙还是踏出与之前完全不同的一步呢?
自出生尔来,每个人的际遇不同从而塑造了不一样的人格和性格。对于事物和道理的意识到和行动到,两者之间永远有差距。我于之前环境和周遭所赋予我的,暂且只是记录和要求自己改变,受现实的曲折,行赤子之心,结果云云。改变和成长是多么有趣的词语,让人生的体验更为完好,想改变吗?想,如同孤独形影相随。《那年那兔那些事儿》告诉我,理想从现在开始就会早一步实现。那么如何改变?以同理之心,上善若水,心诚而明理,不妄自菲薄,亦不增长自负之心。《认知的尺度》引用“最难的事情是认清自己”,告诉我了解了自己是最有意思的事情啊。 一时迷失,一时失去,一时怨恨,患得患失。这样的人又如何担起大任来呢?却也承受精神之涣散,肉体之酸痛,明力之未逮,失道以无能。矛盾啊,我本是平凡之人,却强迫自己如神仙下凡一般,动指而移山,挥手而洒雨,飞天而畅游,遁地如游戏。其种种,不如相信积累的力量,要说结果,总得畜够结果的养分啊。

计划篇

回顾所学,自我 Debug 之余,我想一个败笔就是没有好好地把写过的东西进行汇总和整理。现我有一点空余,不如整理、反思和改进。maven的 jira 账号已经注册了太久了是时候发起 issue 了。

Java Web 整理

概念: contract:指以特定的对象来实现特定的重复,这个重复具备稳定的约束。 包: basic-contract:基础约束,这个约束处理的是 controller 中一些重复使用的部分。(即将发布 Jar 包到中央仓库)
mybatis-contract:mybatis 约束,定制了自己常用的 关于 mybatis 的约束,如分页器的复用,Mapper 一些常常出现的方法。
content-middleware:企图把 内容作为一个单独的模块抽象出来。