理解SpringMvc架构以及流程

本文是基于慕课网小马哥的 《Spring Boot 2.0深度实践之核心技术篇》的内容结合自己的需要和理解做的笔记。

理解 Spring Web MVC 架构

在解释Spring Web Mvc 的架构之前,首先我们要了解一下基于Servlet的基础架构之上构建的一种J2EE的设计模式--Front Controller(前端总控制器模式)

前端总控制器.png

通过流程图我们可以了解到:

  1. 当客户端发送请求到前端总控制器(Front Controller),在这里前端总控制器有两种实现《Servlet》和《JSP》。
  2. 前端总控制器通过委派发送给应用控制器(ApplicationController)。
  3. 应用控制器通过委派调用一个命令对象(Command),然后分发给对应的视图处理器(View)。

介绍完前端总控制器,接下来对于理解SpringMvc架构就很简单了。

mvc架构图.png

通过流程图我们可以了解到:

  1. 通过客户端发送的请求到前端总控制器(Front Controller) 也就是我们经常提到的DispatcherServlet。
  2. 前端总控制器委派给Controller,也就是我们常用的SpringMVC使用的控制层,也就是标注@Controller的业务模型。
  3. 然后通过DispatcherServlet将业务处理的结果委派给视图渲染模型。
  4. 最终返回视图对象给DispatcherServlet,响应结果。

SpringMvc核心组件以及交互流程

SpringMvc核心组件

  • 处理器管理
    • 映射:HandlerMapping
    • 适配:HandlerAdapter
    • 执行:HandlerExecutionChain
  • 渲染
    • 视图解析:ViewResolver
    • 国际化: LocaleResolver,LocaleContextResolver
    • 个性化:ThemeResolver
  • 异常处理
    • HandlerExceptionResolver

各个组件的介绍内容在Web on Servlet Stack中的 1.2.2. Special Bean Types章节中。

SpringMvc交互流程

交互流程.png

流程说明

  1. 用户发送请求至前端控制器 DispatcherServlet。
  2. DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。处理器映射器根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
  3. DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器。
  4. HandlerAdapter 执行Controller层中的对应方法。
  5. Controller 执行完成返回 ModelAndView或ViewName。 HandlerAdapter 将 handler 执行结果 ModelAndView 返回给 DispatcherServlet。
  6. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
  7. ViewReslover 解析后返回具体 View 对象。 DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)。
  8. DispatcherServlet 响应用户。

源码理解交互流程

准备一个简单的SpringMVC项目

在Debugger源码之前,先让我们创建一个简单的spring项目,只需要一个简单的请求返回一个JSP页面就可以。

我创建的spring项目全部都使用了注解驱动(版本必须是Spring Framework 3.1 +,Servlet 3.0)。

在这里不阐述是如何实现自动装配Spring MVC项目的,后面会有单独的介绍,先简单给出代码让我们的程序跑起来,方便一步一步Debugger。

项目目录如下:

项目目录.png

pom.xml


   org.springframework.boot
   spring-boot-starter-parent
   2.0.4.RELEASE
    

这里需要注意一下 我使用的是IntelliJ IDEA 商业版,所以不需要内置tomcat插件。

WEB-INF/jsp/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title


Hello World

替换web.xml的配置类 --DefaultAnnotationConfigDispatcherServletInitializer

package com.web.servlet.support;

import com.web.configuration.DispatchServletConfiguration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

/**
 * web.xml的配置
 */
public class DefaultAnnotationConfigDispatcherServletInitializer
        extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class[] getServletConfigClasses() {    //配置DispatcherServlet
        return new Class[]{DispatchServletConfiguration.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

替换application-context.xml的配置类 --WebMvcConfig

package com.web.configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * {@link DispatcherServlet}
 *  扫描使用注解驱动的包  
 * 相当于 
 */
@ComponentScan(basePackages = "com.web")  
public class DispatchServletConfiguration  {
}

以上的XML配置和java代码通过对应的注释已经一一对应解释了。

简单的Controller层 --HelloWorldController

package com.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 简单controller
 */
@Controller
public class HelloWorldController {
    @RequestMapping("/index")
    public String index() {
        return "index";
    }
}

这里全部代码已经给出。下面让我们开始调试吧。让我们启动tomcat,访问http://localhost:8080/index。

执行流程

流程的核心就是 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);

         // Determine handler for the current request.
         // 确定当前请求的处理器也就是各种HandlerMapping
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
         // 确定当前请求的适配器
         HandlerAdapter 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()) {
               logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
            }
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

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

         // Actually invoke the handler.
         // 调用实际的方法
         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);
         }
      }
   }
}

根据上面的源码我们可以看到核心的过程

     // Determine handler for the current request.
     // 确定当前请求的处理器也就是各种HandlerMapping
     mappedHandler = getHandler(processedRequest);
     
      // Determine handler adapter for the current request.
     // 确定当前请求的适配器
     HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         
     // Actually invoke the handler.
     // 调用实际的方法
     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

现在让我们分步来debugger这几步,一点一点理解其流程。

1.org.springframework.web.servlet.DispatcherServlet#getHandler

gethandler.png

我们可以看到spring默认实现了 HandlerMapping的5个实现类,而第一个就是我们配置常用的 RequestMappingHandlerMapping

接着往下看,通过RequestMappingHandlerMapping 获取当前请求的处理程序执行链也就是 HandlerExecutionChain 包含当前处理程序对象和任何处理程序拦截器。

executionChain.png

我们可以清楚的看到,我们请求的URL路径直接映射到了public java.lang.String com.web.controller.HelloWorldController.index() 这个方法。

2.org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

gethandler.png

我们可以看到spring默认实现了 HandlerAdapter的3个实现类,而第一个就是我们配置常用的RequestMappingHandlerAdapter

然后往下Debugger

getHandlerAdapt2.png

这一步是判断当前的HandlerMethod是否是期望实现的处理程序。最后返回对应的适配处理器。

3.org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle

handle.png

使用给定的适配处理程序来处理此请求,返回ModelAndView对象,这里viewName是 “index”,我们可以对应刚刚我们写的HelloWorldController#index()方法,确实方法返回的就是"index" 字符串。

4.org.springframework.web.servlet.DispatcherServlet#processDispatchResult

这个方法的目的就是 解析程序选择处理或者处理程序调用的结果无论是ModelAndView或者Exception。

render.png

最后通过render方法来渲染视图,我们在进入render方法中看一下就可以查找到我们想要的org.springframework.web.servlet.view.JstlView 和对应返回的资源URL。

view.png

至此,SpringMvc大致的执行流程已经介绍完了。我们通过Debugger源码再对应上面的交互流程图,就可以理解SpringMVC的架构和流程了。

DEMO地址

你可能感兴趣的:(理解SpringMvc架构以及流程)