一、概述

MVC全称为Model View Controller,是一种用于创建Web应用程序表现层的设计模式。今天我们从源码的角度介绍一下Spring MVC。


二、项目构建

和之前介绍Spring框架一样,我们要了解Spring MVC的运行原理需要先搭建一个简单的项目,通过项目来加深自己对该框架的理解。下面我们搭建一个简单的Spring MVC项目,如下图

springmvc 参数处理源码 深入理解spring mvc源代码_配置文件

我们先运行代码,看看Spring MCV架构的优势和特点在哪里!我们先看看该项目的代码量,通过下图我们可以看到除了配置文件以外,我们只写了DemoController这一个类便可以请求到success.jsp这个页面。如果我们要想再访问一个页面,只需要在该类里再添加一个方法用@RequestMapping注解标记即可。

springmvc 参数处理源码 深入理解spring mvc源代码_MVC_02

我们再看下之前的spring框架怎么进行访问的……如下图

springmvc 参数处理源码 深入理解spring mvc源代码_MVC_03

该访问即为最初的访问方式,通过实现HttpServlet,重写doGet和doPost方法来实现访问,这种方式每一个请求都要有一个具体的类去实现HttpServlet,重写里面的doPost和doGet方法做具体的业务处理……

通过这两种方式的比较,我们可以很轻易的看出Spring MVC解放了生产力,让开发者可以花更少的时间去实现更多的价值。那么它究竟是如何实现的呢?我们接下来通过阅读源码的方式来了解一下它的运行原理。


三、源码剖析

同样和解析Spring框架时的做法一样,我们先观察web.xml配置文件

springmvc 参数处理源码 深入理解spring mvc源代码_配置文件_04

从配置文件中我们可以看到里面配置了一个servlet和servlet-mapping,servlet类为DispatcherServlet,参数为我们的springmvc.xml配置文件;servlet-mapping配置了路径的映射,然后我们再来看看springmvc.xml文件的内容

springmvc 参数处理源码 深入理解spring mvc源代码_配置文件_05

我们配置了注解扫描和视图解析器,了解完配置文件后,我们来解析下DispatcherServlet这个类,其实该类的核心是doService方法如下图

springmvc 参数处理源码 深入理解spring mvc源代码_sed_06

doService方法的核心又是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);

        // 获取处理当前请求的handler处理器
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
          // 没有获取到handler处理器,就返回404
          noHandlerFound(processedRequest, response);
          return;
        }

        // 获取当前请求的处理程序适配器。
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 处理 last-modified 请求头
        String method = request.getMethod();
        boolean isGet = "GET".equals(method);
        if (isGet || "HEAD".equals(method)) {
          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return;
          }
        }

        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // 实际处理器处理请求,返回 ModelAndView 对象
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }
        // 对结果的视图对象进行处理
        applyDefaultViewName(processedRequest, mv);
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
        dispatchException = ex;
      }
      catch (Throwable err) {
        // As of 4.3, we're processing Errors thrown from handler methods as well,
        // making them available for @ExceptionHandler methods and other scenarios.
        dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      // 跳转页面渲染视图
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
          new NestedServletException("Handler processing failed", err));
    }
    finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
        // Instead of postHandle and afterCompletion
        if (mappedHandler != null) {
          mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        }
      }
      else {
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
          cleanupMultipart(processedRequest);
        }
      }
    }
  }

Spring MVC的核心思想全部在这个方法里面,先通过扫描获取@Controller标记的类,即handler,没找到则404报错,通过适配器找到能处理该请求的处理器,然后实际请求返回ModelAndView对象,最后对该对象进行处理,渲染,然后跳转到该页面。


四、小结

本章我们简单的构建了一个Spring MVC项目,发现使用该框架可以避免频繁的创建servlet,减少开发者的工作量。通过解读DispatcherServlet这个类,我们从整体上知道了Spring MVC的运作模式,下一篇文章我们就详细的解读一下handler和handlerAdapter是怎么运作的。


感兴趣的读者朋友可以 关注本公众号,和我们一起学习探究。


springmvc 参数处理源码 深入理解spring mvc源代码_MVC_07


本人因所学有限,如有错误之处,望请各位指正!