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 对象。
(2)点击 step over 前进,可以看到这里的 getWebApplicationContext() 就是 spring 容器对象。也就是说,当接收到url请求的时候,前端控制器就会进行spring 容器的初始化,将各种 bean 放入到容器中。
(3)点击 step over,在经过一系列处理之后调用了 doDispatch() 方法。这是一个核心的方法。
(4)点击 step into,进入 doDispatch() 方法,该方法中首先定义了一个处理器执行链,它用于存放拦截器(多个)和目标 Handler。然后定义了ModelAndView 对象,用于存放视图信息和数据。
执行下面的语句后,就通过映射拿到了处理器执行链 HandlerExecutionChain 的目标 Handler 和拦截器链
(5)点击 step over,跳到如下:获取适配器,包含要执行的目标 Handler
(6)点击 step over,跳到如下:调用 handle() 方法,进行反射调用目标 Handler。
(7)点击 step into,进入到 handle 方法中调用的 handleInternal() 方法,handleInternal() 方法中反射调用了目标 Handler的方法,然后返回视图对象。
(8)在目标方法中打上断点,点击 resume 进入目标方法
(9)点击 step over,ModelAndView 对象返回给适配器。
(10)然后返回到第7步的方法中,因此第7步中返回的视图就是目标方法操作后返回的视图对象
(11)继续一直点击 step over,方法 return 返回到第6步,将获取的视图对象返回给前端控制器。
(12)点击 step over,在前端控制器的 doService 方法中执行如下语句,processDispatchResult() 方法对前面返回的视图进行解析。
(13)step into 进入processDispatchResult 方法,该方法调用 render() 进行渲染。
(14)render() 方法从 ModelAndView 对象中得到视图名称 viewName,如果 viewName 不为空,就进行视图解析。
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;
}
(15)返回前端控制器之后,又调用 View的 render() 方法进行视图的渲染。
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() 方法通过请求转发跳转到相应页面:
(16)最后 tomcat 返回 http响应,浏览器显示页面
3.练习
- 将之前的 SpringMVC 异常处理相关代码和案例写一遍
- 简述原生的 SpringMVC 执行流程,并画出示意图
- debug SpringMVC 的执行流程源码,加深理解