2019独角兽企业重金招聘Python工程师标准>>>

一、简介

Spring MVC框架在工作中经常用到,配置简单,使用起来也很方便,很多书籍和博客都有介绍其处理流程,但是,对于其原理,总是似懂非懂的样子。我们做技术,需要做到知其然,还要知其所以然。今天我们结合源码来深入了解一下Spring MVC的处理流程。

以上流程图是Spring MVC的处理流程(参考:spring-mvc-flow-with-example),原作者对流程的解释如下:

Step 1: First request will be received by DispatcherServlet.Step 2: DispatcherServlet will take the help of HandlerMapping and get to know the Controller class name associated with the given request.Step 3: So request transfer to the Controller, and then controller will process the request by executing appropriate methods and returns ModelAndView object (contains Model data and View name) back to the DispatcherServlet.Step 4: Now DispatcherServlet send the model object to the ViewResolver to get the actual view page.Step 5: Finally DispatcherServlet will pass the Model object to the View page to display the result.

针对以上流程,这里需要更加详细一点:

1、请求被web 容器接收,并且根据contextPath将请求发送给DispatcherServlet

2、DispatcherServlet接收到请求后,会设置一些属性(localeResolver、themeResolver等等),在根据request在handlerMappings中查找对应的HandlerExecutionChain;然后根据HandlerExecutionChain中的handler来找到HandlerAdapter,然后通过反射来调用handler中的对应方法(RequestMapping对应的方法)

3、handler就是对应的controller,调用controller中的对应方法来进行业务逻辑处理,返回ModelAndView(或者逻辑视图名称)

4、ViewResolver根据逻辑视图名称、视图前后缀,来获取实际的逻辑视图

5、获取实际视图之后,就会使用model来渲染视图,得到用户实际看到的视图,然后返回给客户端。

二、Demo样例

我们运行一个小样例(github地址:https://github.com/yangjianzhou/spring-mvc-demo)来了解Spring MVC处理流程,项目结构如下:

web.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:/applicationContext.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>smart</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>smart</servlet-name><url-pattern>/</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>

smart-servlet.xml的内容如下:

 <context:component-scan base-package="com.iwill.mvc"/><!-- 在使用Excel PDF的视图时,请先把这个视图解析器注释掉,否则产生视图解析问题--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"p:order="100" p:viewClass="org.springframework.web.servlet.view.JstlView"p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>

UserController.java的代码如下:

package com.iwill.mvc;import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;@Controller
@RequestMapping("/user")
public class UserController {Logger logger = Logger.getLogger(UserController.class);@RequestMapping("register")public String register() {logger.info("invoke register");return "user/register";}@RequestMapping(method = RequestMethod.POST)public ModelAndView createUser(User user) {ModelAndView mav = new ModelAndView();mav.setViewName("user/createSuccess");mav.addObject("user", user);return mav;}
}

三、请求接收

DispatcherServlet的类继承关系如下:

可以看出,DispatcherServlet是一个HttpServlet,因此,它可以处理http请求。

在浏览器中输入http://localhost:8080/spring-mvc-demo/user/register,因为在web服务器上配置了spring-mvc-demo的contextPath为spring-mvc-demo,所以/spring-mvc-demo/user/register的请求就会被DispatcherServlet处理,请求处理路径如下:

请求由tomcat传递给了DispatcherServlet了,DispatcherServlet接收后,就开始自己的特殊处理了。

红框中是Spring MVC自己特有的逻辑,主要是与视图、主题有关。

接下来的主要处理逻辑在org.springframework.web.servlet.DispatcherServlet#doDispatch中:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = processedRequest != request;// 根据request在handlerMappings中获取HandlerExecutionChainmappedHandler = getHandler(processedRequest, false);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}//根据handler在handlerAdapters中获取HandlerAdapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {String requestUri = urlPathHelper.getRequestUri(request);logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}try {//适配器调用实际的handlermv = ha.handle(processedRequest, response, mappedHandler.getHandler());}finally {if (asyncManager.isConcurrentHandlingStarted()) {return;}}applyDefaultViewName(request, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}//逻辑视图名转换为物理视图名,并进行视图渲染processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Error err) {triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionmappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);return;}// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}

首先获取HandlerExecutionChain(入口:mappedHandler = getHandler(processedRequest, false);),方法如下:

   protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {for (HandlerMapping hm : this.handlerMappings) {if (logger.isTraceEnabled()) {logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");}HandlerExecutionChain handler = hm.getHandler(request);if (handler != null) {return handler;}}return null;}

之后就是根据handler获取HandlerAdapter(入口:HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())):

  protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {for (HandlerAdapter ha : this.handlerAdapters) {if (logger.isTraceEnabled()) {logger.trace("Testing handler adapter [" + ha + "]");}if (ha.supports(handler)) {return ha;}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}

handler适配器调用handler的方法(入口:mv = ha.handle(processedRequest, response, mappedHandler.getHandler())):

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);Method handlerMethod = methodResolver.resolveHandlerMethod(request);ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);ServletWebRequest webRequest = new ServletWebRequest(request, response);ExtendedModelMap implicitModel = new BindingAwareModelMap();Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);ModelAndView mav =methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);return mav;}

通过Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel)会调用底层的方法:

红框中,通过反射调用UserController的register方法。这样请求就被传递到了实际的controller方法了。

四、响应返回

UserController#register处理后,就返回逻辑视图名:user/register。在org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.ServletHandlerMethodInvoker#getModelAndView中,就会将String转化为ModelAndView:

在org.springframework.web.servlet.DispatcherServlet#render中,就会将逻辑视图ModelAndView转化物理视图。

resolveViewName就是使用ViewResolver来获取物理视图名:

 protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,HttpServletRequest request) throws Exception {for (ViewResolver viewResolver : this.viewResolvers) {View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}return null;}

物理视图名会被缓存,不需要重复解析,提高性能。

这里就是prefix和suffix的用途了,用于定位实际视图。

获取到了物理视图之后,就进行视图渲染了。

针对jsp格式的视图,我们配置的view是org.springframework.web.servlet.view.JstlView,渲染过程就是将model中的值set到request的attribute中,之后就是使用jsp自己的规则来显示jsp文件就好。

转载于:https://my.oschina.net/yangjianzhou/blog/3037873

Spring MVC请求处理流程分析相关推荐

  1. java处理请求的流程_Java Spring mvc请求处理流程详解

    Spring mvc请求处理流程详解 前言 spring mvc框架相信很多人都很熟悉了,关于这方面的资料也是一搜一大把.但是感觉讲的都不是很细致,让很多初学者都云里雾里的.本人也是这样,之前研究过, ...

  2. 初探Spring MVC 请求处理流程

    Spring MVC 相对于前面的章节算是比较简单的,我们首先引用<Spring in Action>上的一张图来了解Spring MVC 的核心组件和大致处理流程: 从上图中看到 ①.D ...

  3. Spring MVC请求处理流程

    从web.xml中 servlet的配置开始, 根据servlet拦截的url-parttern,来进行请求转发 Spring MVC工作流程图 图一 图二  Spring工作流程描述 1. 用户向服 ...

  4. 精尽Spring MVC源码分析 - 一个请求的旅行过程

    我们先来了解一个请求是如何被 Spring MVC 处理的,由于整个流程涉及到的代码非常多,所以本文的重点在于解析整体的流程,主要讲解 DispatcherServlet 这个核心类,弄懂了这个流程后 ...

  5. ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

    开篇:上一篇我们了解了在WebForm模式下一个Page页面的生命周期,它经历了初始化Init.加载Load以及呈现Render三个重要阶段,其中构造了页面控件树,并对页面控件树进行了大量的递归操作, ...

  6. java request 处理过程_小猿圈Java开发之从代码看spring mvc请求处理过程

    原标题:小猿圈Java开发之从代码看spring mvc请求处理过程 Java作为编程界的常青树,有自己生存的独到之处,小猿圈java讲师今天就分享一个关于从代码看spring mvc请求处理过程,通 ...

  7. Spring MVC 请求处理过程。你这样回答保证通过面试!

    前言 SpringMVC 请求处理相信大家都很熟悉了,本篇主要是基于 SpringMVC 处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题. 关于 Spring MVC 的流程思维 ...

  8. spring MVC请求处理类注解属性详解

    spring MVC请求处理类注解属性详解

  9. 详述 Spring MVC 启动流程及相关源码分析

    文章目录 Web 应用部署初始化过程(Web Application Deployement) Spring MVC 启动过程 Listener 的初始化过程 Filter 的初始化 Servlet ...

最新文章

  1. VClient 无法连接Vcenter
  2. 两个例子详解并发编程的可见性问题和有序性问题,通过volatile保证可见性和有序性以及volatile的底层原理——缓存一致性协议MESI和内存屏障禁止指令重排
  3. Eclipse里web的依赖工程部署的简便方法
  4. ubuntu - 安装hive
  5. Memcached简介
  6. java日期加减秒_Java日期——年、月、日、时、分、秒、周加减计算
  7. java list有序还是无序_最详细的Java学习点知识脑图,从基础到进阶,看完还有啥你不懂的...
  8. ajax get请求成功,成功()函数的AJAX GET请求
  9. Android 系统(260)---Android 读取SIM卡参数
  10. Gauss Elimination算法分析与实现
  11. 【POJ1276】【多重背包】凑货币问题
  12. 安装TeamViewer的监视器驱动后屏幕亮度无法调节
  13. gnu coreutils4.5.1 hostid.c源码解读
  14. HTML获取当前IP和当前位置
  15. HDU - 1873 看病要排队(优先队列)
  16. SAP ST05 追踪找表法
  17. Java 获取Word批注所标记的文本和图片
  18. PyTorch 简介
  19. 小论文投稿经历与经验
  20. python m4a转mp3_如何将蜻蜓fm文件转化为音频文件

热门文章

  1. JavaScript创建对象几种形式
  2. html track标签,HTML track标签
  3. java入门申请,《java入门如此简单》——基础知识1
  4. 若依前后端分离版本如何使用Swagger
  5. Spring Boot+Ext JS准前后端框架应用的会话(Session)处理
  6. Weblogic负载均衡/Session复制之集群架构续
  7. [概率论与数理统计] 常用定义与公式
  8. 易语言html实现报表打印,易语言报表统计功能例程可打印
  9. Eclipse install new software 失败 解决方案
  10. 智能跳过节假日算法java_Quartz 定时任务使用 —— 排除指定节假日时间执行任务(十一)...