Spring MVC 源码分析

简介: Spring MVC 源码分析

1. 回顾Servlet

1.1. 什么是Servlet

Servlet is an API that provides many interfaces and classes including documentation.


Servlet is an interface that must be implemented for creating any Servlet.


Servlet is a class that extends the capabilities of the servers and responds to the incoming requests. It can respond to any requests.


其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类。


1.2. Servlet工作模式

  1. 客户端发送请求至服务器;
  2. 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器;
  3. 服务器将响应返回客户端;


1.3. Servlet的工作原理

  1. Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。
  2. 用户请求致使Servlet容器调用Servlet的service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。
  3. ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。
  4. 对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。

1.4. 源码分析

1.4.1. Servlet接口

最原始的Servlet接口有三个主要方法:init、service和destroy;

/**
 * Defines methods that all servlets must implement.
 *
 * @author  Various
 *
 * @see   GenericServlet
 * @see   javax.servlet.http.HttpServlet
 *
 */
public interface Servlet {
    /**
     * Called by the servlet container to indicate to a servlet that the 
     * servlet is being placed into service.
     */
    public void init(ServletConfig config) throws ServletException;
    /**
     * Returns a {@link ServletConfig} object, which contains
     * initialization and startup parameters for this servlet.
     */
    public ServletConfig getServletConfig();
    /**
     * Called by the servlet container to allow the servlet to respond to 
     * a request.
     */
    public void service(ServletRequest req, ServletResponse res)
  throws ServletException, IOException;
    /**
     * Returns information about the servlet, such
     * as author, version, and copyright.
     */
    public String getServletInfo();
    /**
     * Called by the servlet container to indicate to a servlet that the
     * servlet is being taken out of service. 
     */
    public void destroy();
}

1.4.2. GenericServlet抽象类

  1. GenericServlet defines a generic, protocol-independent servlet.


GenericServlet抽象类包含以下主要方法,可以看出主要还是围绕着init、service和destroy;

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
  /**
    * Called by the servlet container to indicate to a servlet that the
    * servlet is being taken out of service.  See {@link Servlet#destroy}.
    */
   public void destroy() { }
   /**
    * Called by the servlet container to indicate to a servlet that the
    * servlet is being placed into service.  See {@link Servlet#init}.
    */
   public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
   }
   /**
    * A convenience method which can be overridden so that there's no need
    * to call <code>super.init(config)</code>.
    */
   public void init() throws ServletException { }
   /**
    * Called by the servlet container to allow the servlet to respond to
    * a request.  See {@link Servlet#service}.
    */
   public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
   /**
    * Returns the name of this servlet instance.
    */
   public String getServletName() {
       ServletConfig sc = getServletConfig();
       if (sc == null) {
           throw new IllegalStateException(
               lStrings.getString("err.servlet_config_not_initialized"));
       }
       return sc.getServletName();
   }
}


1.4.3. HttpServlet抽象类

  1. HttpServlet provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site.

  2. HttpServlet的主要内容如下,主要是提供了service方法,使得继承该类的方法无需再override。定义了doGet、doPost等一系列方法,但并未给出具体实现。
public abstract class HttpServlet extends GenericServlet implements java.io.Serializable {
   /**
    * Called by the server (via the <code>service</code> method) to
    * allow a servlet to handle a GET request. 
    */
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String protocol = req.getProtocol();
     String msg = lStrings.getString("http.method_get_not_supported");
     if (protocol.endsWith("1.1")) {
         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
     } else {
         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
     }
   }
   protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     NoBodyResponse response = new NoBodyResponse(resp);
     doGet(req, response);
     response.setContentLength();
   }
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String protocol = req.getProtocol();
     String msg = lStrings.getString("http.method_post_not_supported");
     if (protocol.endsWith("1.1")) {
         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
     } else {
         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
     }
   }
   /**
    *
    * Receives standard HTTP requests from the public
    * <code>service</code> method and dispatches
    * them to the <code>do</code><i>XXX</i> methods defined in 
    * this class. This method is an HTTP-specific version of the 
    * {@link javax.servlet.Servlet#service} method. There's no
    * need to override this method.
    */
   protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String method = req.getMethod();
     if (method.equals(METHOD_GET)) {
         long lastModified = getLastModified(req);
         if (lastModified == -1) {
       // servlet doesn't support if-modified-since, no reason
       // to go through further expensive logic
       doGet(req, resp);
         } else {
       long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
       if (ifModifiedSince < (lastModified / 1000 * 1000)) {
           // If the servlet mod time is later, call doGet()
                       // Round down to the nearest second for a proper compare
                       // A ifModifiedSince of -1 will always be less
           maybeSetLastModified(resp, lastModified);
           doGet(req, resp);
       } else {
           resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
       }
         }
     } else if (method.equals(METHOD_HEAD)) {
         long lastModified = getLastModified(req);
         maybeSetLastModified(resp, lastModified);
         doHead(req, resp);
     } else if (method.equals(METHOD_POST)) {
         doPost(req, resp);
     } else if (method.equals(METHOD_PUT)) {
         doPut(req, resp);  
     } else if (method.equals(METHOD_DELETE)) {
         doDelete(req, resp);
     } else if (method.equals(METHOD_OPTIONS)) {
         doOptions(req,resp);
     } else if (method.equals(METHOD_TRACE)) {
         doTrace(req,resp);
     } else {
         //
         // Note that this means NO servlet supports whatever
         // method was requested, anywhere on this server.
         //
         String errMsg = lStrings.getString("http.method_not_implemented");
         Object[] errArgs = new Object[1];
         errArgs[0] = method;
         errMsg = MessageFormat.format(errMsg, errArgs);
         resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
     }
   }
   /**
    * Dispatches client requests to the protected
    * <code>service</code> method. There's no need to
    * override this method.
    */
   public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
     HttpServletRequest request;
     HttpServletResponse  response;
     try {
         request = (HttpServletRequest) req;
         response = (HttpServletResponse) res;
     } catch (ClassCastException e) {
         throw new ServletException("non-HTTP request or response");
     }
     service(request, response);
   }
}


1.5. Servlet的局限性

Servlet如果需要给客户端返回数据,比如一个HTML文件,需要一行一行的把HTML语句给用Writer输出,早期简单的网页还能应付得住,但是随着互联网的不断发展,网站的内容和功能越来越强大,一个普通的HTML文件可能就达到好几百行,如果在采用使用Servlet去一行一行的输出HTML代码的话,将会非常的繁琐并且浪费大量的时间,且在当时,出现了PHP这种可以内嵌到HTML文件的动态语言,使得制作动态网页变得异常的简单和轻松,因此大量的程序员转上了PHP语言的道路,JAVA的份额急剧减小,当时JAVA的开发者Sun公司为了解决这个问题,也开发出了自己的动态网页生成技术,使得同样可以在HTML文件里内嵌JAVA代码,这就是现在的JSP技术。


2. Spring MVC简介

2.1. 什么是MVC

  1. MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。是将业务逻辑、数据、显示分离的方法来组织代码。MVC主要作用是降低了视图与业务逻辑间的双向耦合。
  2. Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
  3. View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
  4. Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。
  5. 流程:
  6. 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收;
  7. Controller调用相应的Model层处理请求,处理完毕后结果返回到Controller;
  8. Controller再根据请求处理的结果找到对应的View视图,渲染数据后最终响应给浏览器;


  1. MVC不是一种设计模式,而是一种架构模式。

1.架构模式:一个架构模式描述软件系统里的基本的结构组织或纲要;架构模式提供一些事先定义好的子系统,指定他们的责任,并给出把他们组织在一起的法则和指南;

2.设计模式:一个设计模式提供一种提炼子系统或软件系统中的组织或者他们之间的关系的纲要设计;设计模式描述普遍存在的在相互通信的组件中重复出现的结构,这种结构解决在一定的背景中的具有一般性的设计问题;


2.2. 什么是Spring MVC?


  1. Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。

2.3. Spring MVC核心组件

  1. DispatcherServlet:负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Spring MVC的核心模块;
  2. Handler:处理器,完成具体的业务逻辑,相当于Servlet;
  3. HandlerMapping:DispatcherServlet是通过 HandlerMapping把请求映射到不同的Handler;
  4. HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成;
  5. HandlerExecutionChain:处理器执行链,包括两部分内容:Handler和HandlerInterceptor(系统会有一个默认的HandlerInterceptor,如果有额外拦截处理,可以添加拦截器进行设置);
  6. HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型转换、把表单数据封装到POJO等,这些一系列的操作都是由HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler;
  7. ModelAndView:封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet;
  8. ViewResolver:视图解析器,DispatcherServlet通过它把逻辑视图解析为物理视图,最终把渲染的结果响应给客户端;

2.4. Spring MVC工作流程

  1. 客户端向服务器发送请求,请求被 SpringMVC 前端控制器 DispatcherServlet 捕获;
  2. DispatcherServlet 根据该 URI,调用 HandlerMapping 获得该 Handler 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 的形式返回;
  3. DispatcherServlet 根据获得的 Handler,选择一个合适的 HandlerAdapter;
  4. 如果成功获得 HandlerAdapter,此时将开始执行拦截器的 preHandler方法;
  5. 提取 Request 中的数据,填充 handle入参,开始执行 handle 方法,处理请求,在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:

1.HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换成一个对象,将对象转换为指定的类型信息;

2.数据转换:对请求消息进行数据转换。如 String 转换成 Integer、Double 等;

3.数据格式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等;

4.数据验证:验证数据的有效性(长度、格式等),验证结果存储到 BindingResult 或 Error 中;

6.handle方法执行完毕后,返回一个ModelAndView对象给DispatcherServlet;

7.此时将开始执行拦截器的 postHandle 方法【逆向】;

8.DispatcherServlet把获取的ModelAndView对象传给ViewResolver视图解析器,来渲染视图;

  1. DispatcherServlet把渲染后的视图返回给客户端;
  2. 渲染视图完毕执行拦截器的 afterCompletion 方法【逆向】;


3. Spring MVC源码分析

3.1. FrameworkServlet抽象类

FrameworkServlet is the base servlet for Spring’s web framework. Provides integration with a Spring application context, in a JavaBean-based overall solution.

HttpServlrtBean抽象类:Simple extension of HttpServlet which treats its config parameters (init-param entries within the servlet tag in web.xml) as bean properties.


看一下FrameworkServlet抽象类的主要方法,发现把doXXX方法都统一到doService抽象方法上了;


         


3.2. DispatcherServlet类

1.Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.

DispatcherServlet类override了FrameworkServlet的doService方法,并将最终的处理流程放在了doDispatch方法里,该方法很值得研究,写了一些注释,接下来会详细分析带有标号的注释对应的代码。

/**

3.3. 获取HandlerExecutionChain对象

// 1. 获取HandlerExecutionChain对象
mappedHandler = getHandler(processedRequest);

来看一下getHandler方法:

/**
  1. 遍历了handlerMappings,分别调用它们的getHandler方法,找到对应的HandlerExecutionChain;
  2. HandlerExecutionChain类中主要包含以下三个属性:
// 请


在AbstractHandlerMapping抽象类中,getHandler方法首先调用了getHandlerInternal方法,通过寻找、读取配置文件,找到request对应的handler,然后调用了getHandlerExecutionChain方法,通过路径匹配,找到对应的interceptors;

3.4. 根据Handler获取HandlerAdapter

// 2. 根据Handler获取Handl


来看一下getHandlerAdapter方法:

/**
  1. 遍历handlerAdapters,找到能够处理当前 Handler 的HandlerAdapter,如果没找到会报错;

3.5. 调用拦截器的preHandle方法

//


2.来看下applyPreHandle方法:

/**


  1. 该方法首先循环调用拦截器的preHandle方法,如果某个拦截器的preHandle方法返回 false,则调用triggerAfterCompletion方法,且记录了拦截器的执行位置;
  2. 来看下triggerAfterCompletion方法:
/
  1. 该方法反向依次调用那些 preHandle 方法返回 ture 的拦截器的 afterCompletion 方法;

3.6. 通过HandlerAdapter调用Handler实际处理请求,获取ModelAndView对象


         

来看下handle方法,handle方法会走到org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod:

/**

上述方法主要做了三件事:

  1. 组装目标方法需要的参数;
org.spri


  1. 在该方法中,使用HandlerMethodArgumentResolver进行解析请求,得到参数;
  2. HandlerMethodArgumentResolver有很多实现类,归纳如下:
  3. 1687705929055.png

2.通过反射调用处理请求的目标方法,获取方法的返回值;

org.springframewo

3.对方法的返回值进行处理;

org.s


该接口有两个方法:

/*


2.该接口具有很多实现类:


归纳如下:

1687705978024.png

以其中的RequestResponseBodyMethodProcessor为例,来看下源码:

@O


  1. 注意,并不是所有的实现类的handleReturnValue方法都将结果直接返回给客户端了,有些是需要渲染的;

3.7. 调用拦截器的postHandle方法

// 5.


来看下applyPostHandle方法:

/**


  1. 逆序调用拦截器的postHandle方法;

3.8. 处理分发结果,渲染视图(包含了正常处理和异常情况的处理),将结果输出到客户端

3.8.1. 位置

// 6.


3.8.2. processDispatchResult方法源码

/*


3.8.3. 如果有异常,进行全局异常处理

mv =


看一下processHandlerException方法:

/**


  1. 该方法主要是遍历HandlerExceptionResolver的resolveException方法来处理异常;

3.8.4. 渲染视图

render(m

看一下render方法:

/**

3.该方法主要做了两件事:


1.调用resolveViewName方法解析视图名称得到 View;


1.最终是在org.springframework.web.servlet.view.UrlBasedViewResolver#buildView中拼接了prefix和suffix;

调用render方法渲染视图;

  1. 看一下render方法,发现最终走到


org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel方法中,以其实现类override的方法org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel为例:


         

3.8.5. 调用拦截器的 afterCompletion 方法

if (mappe


2.来看一下这部分源码:

/**


  1. 主要是反向调用拦截器的afterCompletion方法;
目录
相关文章
|
2月前
|
缓存 前端开发 Java
Spring MVC 面试题及答案整理,最新面试题
Spring MVC 面试题及答案整理,最新面试题
97 0
|
2月前
ssm(Spring+Spring mvc+mybatis)——updateDept.jsp
ssm(Spring+Spring mvc+mybatis)——updateDept.jsp
11 0
|
2月前
ssm(Spring+Spring mvc+mybatis)——showDept.jsp
ssm(Spring+Spring mvc+mybatis)——showDept.jsp
9 0
|
2月前
|
SQL JavaScript Java
springboot+springm vc+mybatis实现增删改查案例!
springboot+springm vc+mybatis实现增删改查案例!
26 0
|
2月前
|
SQL Java 数据库连接
挺详细的spring+springmvc+mybatis配置整合|含源代码
挺详细的spring+springmvc+mybatis配置整合|含源代码
45 1
|
18天前
|
数据采集 前端开发 Java
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
数据塑造:Spring MVC中@ModelAttribute的高级数据预处理技巧
23 3
|
18天前
|
存储 前端开发 Java
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
会话锦囊:揭示Spring MVC如何巧妙使用@SessionAttributes
14 1
|
18天前
|
前端开发 Java Spring
数据之桥:深入Spring MVC中传递数据给视图的实用指南
数据之桥:深入Spring MVC中传递数据给视图的实用指南
31 3
|
28天前
|
前端开发 安全 Java
使用Java Web框架:Spring MVC的全面指南
【4月更文挑战第3天】Spring MVC是Spring框架的一部分,用于构建高效、模块化的Web应用。它基于MVC模式,支持多种视图技术。核心概念包括DispatcherServlet(前端控制器)、HandlerMapping(请求映射)、Controller(处理请求)、ViewResolver(视图解析)和ModelAndView(模型和视图容器)。开发流程涉及配置DispatcherServlet、定义Controller、创建View、处理数据、绑定模型和异常处理。
使用Java Web框架:Spring MVC的全面指南
|
2月前
|
敏捷开发 监控 前端开发
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
Spring+SpringMVC+Mybatis的分布式敏捷开发系统架构
84 0