跳转至

SpringMVC执行流程

1.SpringMVC执行流程分析图

例子

(1)创建 HaloHandler

package com.li.web.debug;

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

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 李
 * @version 1.0
 */
@Controller
public class HaloHandler {
    //编写方法,响应请求,返回一个 ModelAndView对象
    @RequestMapping(value = "/debug/springmvc")
    public ModelAndView halo(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView modelAndView = new ModelAndView();
        //对应到 WEB-INF/pages/ok.jsp (ok的前后缀是你在视图解析器中配置的前后缀)
        modelAndView.setViewName("ok");
        //在model中放入数据 k-v,ModelAndView的属性也会被springmvc放入到request域中
        modelAndView.addObject("name", "齐天大圣");
        return modelAndView;
    }
}

(2)创建ok.jsp,作为响应后跳转的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>ok</title>
</head>
<body>
<h1>进入到ok页面</h1>
<h2>name-${requestScope.name}</h2>
</body>
</html>

2.源码debug

我们以上述代码为例子进行源码分析。

(1)在 DispatcherServlet 的 doService 方法中打上断点,在浏览器中访问目标方法,可以看到光标跳转到断点处:当请求发送到服务器时,tomcat 将 http 请求包装成 request 对象,前端控制器 DispatcherServlet 使用 doService() 方法接收这个 request 对象。

image-20230220213910330

image-20230220203713801

(2)点击 step over 前进,可以看到这里的 getWebApplicationContext() 就是 spring 容器对象。也就是说,当接收到url请求的时候,前端控制器就会进行spring 容器的初始化,将各种 bean 放入到容器中。

image-20230220204141473

(3)点击 step over,在经过一系列处理之后调用了 doDispatch() 方法。这是一个核心的方法。

image-20230220204445920

(4)点击 step into,进入 doDispatch() 方法,该方法中首先定义了一个处理器执行链,它用于存放拦截器(多个)和目标 Handler。然后定义了ModelAndView 对象,用于存放视图信息和数据。

image-20230220213959367

image-20230220205326737


执行下面的语句后,就通过映射拿到了处理器执行链 HandlerExecutionChain 的目标 Handler 和拦截器链

image-20230220210019024

(5)点击 step over,跳到如下:获取适配器,包含要执行的目标 Handler

image-20230220210743139

image-20230220214118074

(6)点击 step over,跳到如下:调用 handle() 方法,进行反射调用目标 Handler。

image-20230220211201477

(7)点击 step into,进入到 handle 方法中调用的 handleInternal() 方法,handleInternal() 方法中反射调用了目标 Handler的方法,然后返回视图对象。

image-20230220211812556 image-20230220214704657

(8)在目标方法中打上断点,点击 resume 进入目标方法

image-20230220212955654

(9)点击 step over,ModelAndView 对象返回给适配器。

image-20230220213514030 image-20230220214807691

(10)然后返回到第7步的方法中,因此第7步中返回的视图就是目标方法操作后返回的视图对象

image-20230220215320257

(11)继续一直点击 step over,方法 return 返回到第6步,将获取的视图对象返回给前端控制器。

image-20230220215703488

image-20230220215805035

(12)点击 step over,在前端控制器的 doService 方法中执行如下语句,processDispatchResult() 方法对前面返回的视图进行解析。

image-20230220220003548

(13)step into 进入processDispatchResult 方法,该方法调用 render() 进行渲染。

image-20230220220233835

(14)render() 方法从 ModelAndView 对象中得到视图名称 viewName,如果 viewName 不为空,就进行视图解析。

image-20230220220630961

resolveViewName() 方法进行视图解析,然后返回 view 给前端控制器。

@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}

image-20230220220937160

(15)返回前端控制器之后,又调用 View的 render() 方法进行视图的渲染。

注意和前端控制器的 render 方法区分

image-20230220221538252

image-20230220222131962

View 的 render() 方法:

@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
      HttpServletResponse response) throws Exception {

   if (logger.isDebugEnabled()) {
      logger.debug("View " + formatViewName() +
            ", model " + (model != null ? model : Collections.emptyMap()) +
            (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
   }

   Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
   prepareResponse(request, response);
    //渲染合并输出模型
   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

renderMergedOutputModel() 方法通过请求转发跳转到相应页面:

image-20230220221857739

image-20230220222033914

(16)最后 tomcat 返回 http响应,浏览器显示页面

image-20230220222507677

3.练习

  1. 将之前的 SpringMVC 异常处理相关代码和案例写一遍
  2. 简述原生的 SpringMVC 执行流程,并画出示意图
  3. debug SpringMVC 的执行流程源码,加深理解