源码看SpringMVC工作流程

源码看SpringMVC工作流程

1.1 gradle搭建源码调试环境

1)搭建gradle环境
4个步骤
1、File-New-Module
选择java和web

在这里插入图片描述
2、填写包信息
在这里插入图片描述
3、存储路径
在这里插入图片描述

2)增加起步依赖
依赖的项目,直接复制粘贴上去
1、对spring的依赖
2、对MVC的依赖
3、对Tomcat插件的依赖
build.gradle

group 'com.spring.test'
version '5.0.2.RELEASE'

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'com.bmuschko.tomcat' //tomcat: 插件
// tomcat: 以下配置会在第一次启动时下载插件二进制文件
//在项目根目录中执行gradle tomcatRun
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.bmuschko:gradle-tomcat-plugin:2.5'
    }
}
// 配置阿里源
allprojects {
    repositories {
        maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
    }
}


dependencies {
    testCompile group: 'org.testng', name: 'testng', version: '6.14.3'
    runtime 'javax.servlet:jstl:1.1.2' // Servlet容器必需
    compile(project(':spring-context'))
    compile(project(':spring-web'))
    compile(project(':spring-webmvc'))

    // tomcat: 将Tomcat运行时库添加到配置tomcat中: (此处为Tomcat9)
    def tomcatVersion = '9.0.1'
    tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
            "org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6",
            "org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
}

// tomcat: 一些协议设置(注意,这里必须加上,不然会抛tomcat的异常,仅限tomcat9)
tomcat {
    httpProtocol = 'org.apache.coyote.http11.Http11Nio2Protocol'
    ajpProtocol  = 'org.apache.coyote.ajp.AjpNio2Protocol'
}



// UTF-8
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}


前提:
增加WEB-INF目录和Web.xml
1、打开File - Proect Structrue
2、选中刚才的mvc项目,展开,选中web gradle , 到右边 点击加号
3、确认路径
spring-mvc-test\src\main\webapp\WEB-INF\web.xml

WEB-INF和xml创建完毕
在这里插入图片描述
webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc-servlet.xml</param-value>
            <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>-->
        </init-param>
        <!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法) -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

resources/mvc-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.spring.mvc.test"/>
    <!-- 视图解析器对象 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <!--<property name = "prefix" value="/WEB-INF/"></property>-->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 开启SpringMVC框架注解的支持 -->
    <mvc:annotation-driven/>
    <!--静态资源(js、image等)的访问-->
    <mvc:default-servlet-handler/>

</beans>

webapp/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>SpringMvc源码深入剖析</title>
</head>
<body>
hello ! ${username}
</body>
</html>


MvcController.java

package com.spring.mvc.test;

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

@Controller
public class MvcController {

	@RequestMapping("/index")
	public ModelAndView getModeAndView() {
		//创建一个模型视图对象
		System.out.println("123");
		ModelAndView mav = new ModelAndView("index");
		mav.getModelMap().put("username","administrator");
		return mav;
	}
	@RequestMapping("/text")
	@ResponseBody
	public String text() {
		return "Text...";
	}

}


4)启动MVC项目
两种启动方式
方式一:外挂启动
idea环境外面启动(项目根目录下运行 gradle + task name)

Task NameDescription
tomcatRun启动Tomcat实例并将Web应用程序部署到该实例。
tomcatRunWar启动Tomcat实例并将WAR部署。
tomcatStop停止Tomcat实例
tomcatJasper运行JSP编译器并使用Jasper将JSP页面转换为Java源代码。

在项目根目录中执行 gradle tomcatRun

#动Tomcat实例并将Web应用程序部署到该实例 
gradle tomcatRun 
#停止Tomcat实例 
gradle tomcatStop

控制台正常输出
在这里插入图片描述

方式二:集成到idea中启动
在这里插入图片描述
在这里插入图片描述
即可点击运行
在这里插入图片描述
运行成功
在这里插入图片描述
方式三:
idea右边找到gradle的task,直接双击,这个爽~
在这里插入图片描述
访问MVC项目
注意:spring-test-mvc是项目的名称
http://localhost:8080/spring-test-mvc/index

效果如下
在这里插入图片描述

5)源码调试配置
如何在gradle环境中启用调试模式
重要
想要debug,需要使用上面的方法二,因为debug启动需要设置gradle的环境变量,
即运行gradle命令的时候插入一些参数命令。
增加debug参数,对外暴露5005端口,即监听5005端口。

-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

在这里插入图片描述
在配置Remote;监听5005端口
点击+号,创建Remote;默认配置即可
在这里插入图片描述
最后一步
1、先运行tomcat
2、在运行remote

http://localhost:8080/spring-test-mvc/index

打上断点试试!
包括我们之前ioc里的bean创建等地方,随便打。

1.2 MVC工作原理和继承关系

1)MVC底层工作原理
目标:认识SpringMVC的工作原理(对照源码),如何找到对应的Controller,进行页面渲染的
步骤:11步
源头:http://localhost:8080/spring-test-mvc/index
在这里插入图片描述

SpringMVC工作原理
注意请求不是直接给DispatcherServlet(调度程序)而是通过tomcat转发得到,处理完后转发给tomcat
1、DispatcherServlet(前端控制器) 是个servlet,负责接收Request 并将Request 转发给对应的处理组件。
2、 HanlerMapping (处理器映射器)是SpringMVC 中完成url 到Controller 映射的组件。DispatcherServlet 从HandlerMapping 查找处理Request 的Controller
3、HanlerMapping 返回一个执行器链(url 到Controller 映射的组件)给DispatcherServlet
4、DispatcherServlet请求处理器适配器HandlerAdapter
5、处理器适配器HandlerAdapter去访问我们的handler(controller)
6、handler(controller)返回ModelAndView给处理器适配器HandlerAdapter
7、处理器适配器HandlerAdapter返回ModelAndView给DispatcherServlet
8、DispatcherServlet请求ViewResolver视图解析器
9、ViewResolver视图解析器返回view给DispatcherServlet
10、DispatcherServlet请求view做页面解析和渲染
11、view将渲染好的数据返回给DS,DS将渲染好的字符流给client,看到了页面!

2)MVC核心类继承关系
目标:简单认识MVC的继承关系
在这里插入图片描述
DispatcherServlet 前端总控制器(webmvc源码)
FrameworkServlet (webmvc源码)
HttpServletBean 是的一个简单扩展类((webmvc源码)
HttpServlet(servlet API , 已经离开了spring mvc的控制范围)

1.3 Spring MVC源码深入剖析

当前源码讲解思路
1、断点调试
2、流程图对照
3、继承关系对照

1.3.1 MVC启动阶段

注意,这个阶段没法debug,我们从servlet规范去直接看源码
下面的请求阶段,再详细debug请求链路的完整过程

web.xml回顾

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Spring MVC配置 -->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc-servlet.xml</param-value>
            <!--<param-value>/WEB-INF/mvc-servlet.xml</param-value>-->
        </init-param>
        <!-- load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法) -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

从上面的配置,我们可以看出,web.xml中的DS是一个servlet,那就从java web的servlet规范说起
上面类关系,我们说过,springmvc的范畴里,最顶层的是 HttpServletBean 继承的 标准HttpServlet
1、ioC Bean初始化
org.springframework.web.servlet.HttpServletBean#init
2、9大组件初始化(ioC)
org.springframework.web.servlet.HttpServletBean#init

总结:方法调用关系(伪代码)

HttpServletBean{
    init(){ 
        protected initServletBean(); 
    } 
  }
  FrameworkServlet extends HttpServletBean{ 
    @Override 
    initServletBean(){ 
      initWebApplicationContext(){ 
        WebApplicationContext wac = createWebApplicationContext(rootContext); 
        protected onRefresh(wac); 
      } 
    } 
  }

 DispatcherServlet extends FrameworkServlet{ 
   onRefresh(wac){ 
     initStrategies(wac){ 
        //多文件上传的组件 
        initMultipartResolver(context); 
        //初始化本地语言环境 
        initLocaleResolver(context); 
        //初始化模板处理器 
        initThemeResolver(context); 
        //初始化处理器映射器 
        initHandlerMappings(context); 
        //初始化处理器适配器 
        initHandlerAdapters(context); 
        //初始化异常拦截器 
        initHandlerExceptionResolvers(context); 
        //初始化视图预处理器 
        initRequestToViewNameTranslator(context); 
        //初始化视图转换器 
        initViewResolvers(context); 
        //FlashMap 管理器 
        initFlashMapManager(context); 
     } 
    } 
 }
        

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware

//功能:	SpringMVC初始化配置,最先被执行的方法
	//目标: 设置web.xml中配置的contextConfigLocation属性
	//重点关注:initServletBean();
	@Override
	public final void init() throws ServletException {
		if (logger.isDebugEnabled()) {
			logger.debug("Initializing servlet '" + getServletName() + "'");
		}

		//获得web.xml中的contextConfigLocation配置属性, 是spring MVC的配置(servlet)文件
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);  // ===>
		if (!pvs.isEmpty()) {
			try {  //这里是一堆的初始化操作,不管他,重点在下面!
				//返回一个BeanWrapperImpl实例
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				//获取服务器的各种信息
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				//模板方法,可以在子类中调用,做一些初始化工作,bw代表DispatcherServelt
				initBeanWrapper(bw);
				//将配置的初始化值设置到DispatcherServlet中
				bw.setPropertyValues(pvs, true);
			} catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		//初始化操作,抽象的。子类实现;  【重点关注】 !!!!!!!(ioC 、9大组件)
		initServletBean();   // ===>
		if (logger.isDebugEnabled()) {
			logger.debug("Servlet '" + getServletName() + "' configured successfully");
		}
	}

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

//初始化servlet的bean
	//重点关注:initWebApplicationContext
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
		if (this.logger.isInfoEnabled()) {
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// 这里初始化上下文!!【重要节点】!!!!!(ioC 、9大组件)
			this.webApplicationContext = initWebApplicationContext();   // ===>
			initFrameworkServlet();  // 空的,不管他
		} catch (ServletException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		} catch (RuntimeException ex) {
			this.logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (this.logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - startTime;
			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
					elapsedTime + " ms");
		}
	}

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware

//目标:主要的逻辑就是初始化IOC 容器和9大组件,最终会调用refresh()方法
//重点关注:createWebApplicationContext和onRefresh
	protected WebApplicationContext initWebApplicationContext() {
		// 调用WebApplicationContextUtils来得到根上下文,它保存在ServletContext中
		// 通过servlet的application  listener加载的那些 spring xml,是父容器
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			// 查找已经绑定的上下文,有的话直接用
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			//还是没有的话,开始创建,将rootcontext作为父容器穿进去。
			wac = createWebApplicationContext(rootContext);   // ===>
		}

		if (!this.refreshEventReceived) {
			//9大组件!!【关键步骤】!!!!
			onRefresh(wac);  // ===>
		}
		if (this.publishContext) {
			// 把当前建立的上下文存到ServletContext中,使用的属性名是跟当前Servlet名相关的
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}

		return wac;
	}

接下来应该是初始化,会进createWebApplicationContext这个方法中

//重点关注configureAndRefreshWebApplicationContext
	protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Servlet with name '" + getServletName() +
					"' will try to create custom WebApplicationContext context of class '" +
					contextClass.getName() + "'" + ", using parent context [" + parent + "]");
		}
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException(
					"Fatal initialization error in servlet with name '" + getServletName() +
							"': custom WebApplicationContext class [" + contextClass.getName() +
							"] is not of type ConfigurableWebApplicationContext");
		}
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
		// 设置双亲上下文(也就是根上下文)
		wac.setParent(parent);
		// contextConfigLocation 属性!
		// 也就是web.xml里配置的spring-mvc的xml文件
		String configLocation = getContextConfigLocation();  // ===>  配置的xml文件
		if (configLocation != null) {
			wac.setConfigLocation(configLocation);
		}
		//配置和刷新web应用程序上下文(refresh!!!!!!!)
		configureAndRefreshWebApplicationContext(wac);   // ===>  ioc加载上下文

		return wac;
	}

最终还是走到configureAndRefreshWebApplicationContext来进行初始化操作

//	完成所有bean的解析、加载和初始化
	//重点关注:wac.refresh()
	protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
		//上来一堆设置,不管他,重点在最下面!
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
//				生成WebApplicationContext的id,用于后面加载Spring-MVC的配置文件
				wac.setId(this.contextId);
			} else {
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}
		//设置Servlet
		wac.setServletContext(getServletContext());
		//设置Servlet配置信息
		wac.setServletConfig(getServletConfig());
		//设置命名空间
		wac.setNamespace(getNamespace());
		wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

		// The wac environment's #initPropertySources will be called in any case when the context
		// is refreshed; do it eagerly here to ensure servlet property sources are in place for
		// use in any post-processing or initialization that occurs below prior to #refresh
		ConfigurableEnvironment env = wac.getEnvironment();
		if (env instanceof ConfigurableWebEnvironment) {
			((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
		}
		//后置增强,一般是空的
		postProcessWebApplicationContext(wac);
		//初始化,不管他
		applyInitializers(wac);
		//【重点】调用了ioc的刷新!参考ioc源码课程!!!!!!!!
		wac.refresh();  // ===>  进入ioc的世界!完成bean的载入
	}

接下来就是老伙计了IOC

// 模板方法(抽象类下定义refresh模板),核心方法
	//断点查看
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		// synchronized块锁(monitorenter --monitorexit)
		// 不然 refresh() 还没结束,又来个启动或销毁容器的操作
		//	 startupShutdownMonitor就是个空对象,锁
		synchronized (this.startupShutdownMonitor) {
			//1、【准备刷新】,设置了几个变量,也是准备工作
			prepareRefresh();   //  ===>
			// 2、【获得新的bean工厂】关键步骤,重点!
			//2.1、关闭旧的 BeanFactory
			//2.2、创建新的 BeanFactory(DefaluListbaleBeanFactory)
			//2.3、解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)
			//2.4、返回全新的工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  //  ===> 好戏开始
			//3、【bean工厂前置操作 】为BeanFactory配置容器特性
			// 例如类加载器、表达式解析器、注册默认环境bean、后置管理器
			prepareBeanFactory(beanFactory);   // ===>
			try {
				// 4、【bean工厂后置操作】此处为空方法,如果子类需要,自己去实现
				postProcessBeanFactory(beanFactory);  // ===> 空的!

				//5、【调用bean工厂后置处理器】,开始调用我们自己实现的接口
				//目标:
				//调用顺序一:先bean定义注册后置处理器
				//调用顺序二:后bean工厂后置处理器
				invokeBeanFactoryPostProcessors(beanFactory);  // ===>  重头戏

				//6、【注册bean后置处理器】只是注册,但是还不会调用
				//逻辑:找出所有实现BeanPostProcessor接口的类,分类、排序、注册
				registerBeanPostProcessors(beanFactory);  // ===>  关键点
				// Initialize message source for this context.
				//7、【初始化消息源】国际化问题i18n,参照https://nacos.io/
				initMessageSource(); // ===> 就是往factory加了个single bean

				// Initialize event multicaster for this context.
				//8、【初始化事件广播器】初始化自定义的事件监听多路广播器
				// 如果需要发布事件,就调它的multicastEvent方法
				// 把事件广播给listeners,其实就是起一个线程来处理,把Event扔给listener处理
				// (可以通过 SimpleApplicationEventMulticaster的代码来验证)
				initApplicationEventMulticaster(); // ===> 同样,加了个bean


				// 9、【刷新】这是个protected空方法,交给具体的子类来实现
				//  可以在这里初始化一些特殊的 Bean
				// (在初始化 singleton beans 之前)
				onRefresh();  // ===> 空的!一般没人管它


				//10、【注册监听器】,监听器需要实现 ApplicationListener 接口
				// 也就是扫描这些实现了接口的类,给他放进广播器的列表中
				// 其实就是个观察者模式,广播器接到事件的调用时,去循环listeners列表,
				// 挨个调它们的onApplicationEvent方法,把event扔给它们。
				registerListeners();  // ===> 观察者模式


				//11、 【结束bean工厂初始化操作】
				//1、初始化所有的 singleton beans,反射生成对象/填充
				//2、 调用Bean的前置处理器和后置处理器
				// 关键点:getBean方法里完成
				finishBeanFactoryInitialization(beanFactory);  // ===>  关键点!


				// 12、结束refresh操作
				// 发布事件与清除上下文环境
				finishRefresh();


			} catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				// 销毁已创建的Bean、以免有些 bean 会一直占用资源
				destroyBeans();

				// Reset 'active' flag.
				// 取消refresh操作,重置容器的同步标识。
				cancelRefresh(ex);

				// 把异常往外抛
				throw ex;
			} finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

这里有一点,在初始化后调用finishRefresh()
在refresh完成后发了一个完成初始化的事件

protected void finishRefresh() {
		// 1、清除缓存,其实就是一顿clear
		clearResourceCaches();

		// 2、LifecycleProcessor接口初始化,就是注册了个LifecycleProcessor的bean进去
		// ps:当ApplicationContext启动或停止时,它会通过LifecycleProcessor
		// 来与所有声明的bean的周期做状态更新
		// 而在LifecycleProcessor的使用前首先需要初始化
		initLifecycleProcessor();

		// 3、启动所有实现了LifecycleProcessor接口的bean
		//挨个调它们的start方法。一般没人用
		getLifecycleProcessor().onRefresh();

		// 4、发布一个 ContextRefreshedEvent事件
		//宣布一下,refresh完成了,如果有需要的listener,那就去处理,一般没人关心
		publishEvent(new ContextRefreshedEvent(this));

		// 5、把当前容器注册到到MBeanServer,jmx会用
		LiveBeansView.registerApplicationContext(this);
	}

然后对应的事件被接受到了,在FrameworkServlet

public void onApplicationEvent(ContextRefreshedEvent event) {
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
	}
@Override
	protected void onRefresh(ApplicationContext context) {
		//初始化策略
		initStrategies(context);  // ===>
	}

最终初始化

//一顿初始化……【核心步骤】
	// 其实就是从前面初始化好的context里扒拉出对应类型的bean,然后
	// 将DS里面的对应属性给填充上去,方便下面请求阶段的使用

	// 到这里为止,其实最核心的启动阶段就完成了,需要的东西也都有了。

	protected void initStrategies(ApplicationContext context) {
		//多文件上传的组件
		initMultipartResolver(context);
		//初始化本地语言环境
		initLocaleResolver(context);
		//初始化模板处理器
		initThemeResolver(context);
		//初始化处理器映射器
		initHandlerMappings(context);
		//初始化处理器适配器
		initHandlerAdapters(context);
		//初始化异常拦截器
		initHandlerExceptionResolvers(context);
		//初始化视图预处理器
		initRequestToViewNameTranslator(context);
		//初始化视图转换器
		initViewResolvers(context);
		//FlashMap 管理器
		initFlashMapManager(context);
	}
1.3.2 MVC请求阶段

需求:我们在浏览器输入http://localhost:8080/spring-test-mvc/index,背后到底做了哪些事情
目标:MVC如何通过一个url就能找到我们的controller,并返回数据
1、断点调试 2、流程图对照 3、继承关系对照
流程图解:
代码查找的路径:
在这里插入图片描述
spring mvc的 FrameworkServlet ,这是我们源码跟踪的入口

项目启动
访问

http://localhost:8080/spring-test-mvc/index

入口:开启请求的大门
org.springframework.web.servlet.FrameworkServlet:

//MVC请求入口(浏览器)

//比如,我们在浏览器输入http://test.io/get,背后到底做了哪些事情
//目标:MVC如何通过一个url就能找到我们的controller,并返回数据
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
//请求是否GET
		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		//判断请求方式,发现不是PATCH方式就去调用父类(HttpServlet)中service()方法
		if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
			processRequest(request, response);
		} else {
//			调用用父类中的service方法,
// 调用父类的service 然后正在调用当前类doGet!!!!!!!!!
			super.service(request, response);  // ===> 启程~
		}
	}

java web标准告诉我们,request的get会交给标准 HttpServlet的doGet方法
而这个类FrameworkServlet,是HttpServlet的子类,覆盖了上述的doGet,
所以,请求进入spring的第一入口,就在这里!!!
1)org.springframework.web.servlet.FrameworkServlet#doGet
调用到了org.springframework.web.servlet.FrameworkServlet#doGet

//get请求调用,覆盖了 父类, 也就标准  HttpServlet 的 doGet
	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		processRequest(request, response);    // ===>  get转交给process处理
	}

2)org.springframework.web.servlet.FrameworkServlet#processRequest

//	重点关注:doService
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
// 获取系统当前时间戳
		long startTime = System.currentTimeMillis();
		// 申明一个异常顶层对象Throwable
		Throwable failureCause = null;
// 本地化、国际化处理
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		// 构建一个本地国际化上下文,SimpleLocaleContex
		LocaleContext localeContext = buildLocaleContext(request);
		// 获取当前绑定到线程的RequestAttributes
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		// 构建ServletRequestAttributes
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
		// 将localeContext和requestAttributes放入当前线程中,
		// request作用域每个请求线程都不一样,参数,attribute需要上下文直接传递
		// (回顾:request,session,application)
		initContextHolders(request, localeContext, requestAttributes);  // ===>

		try {
			//前面都是一堆准备工作,重点在这里!跳到DispatcherServlet 类中(子类重写) 【关键点!】
			doService(request, response);  // ===>  进入DS的世界!
		} catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		} catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		} finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				} else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					} else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

3)org.springframework.web.servlet.DispatcherServlet#doService


	//将请求交给doDispatch来处理  重点关注  doDispatch(request, response);
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
					" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
		}

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects
		// 扔一堆东西进request.
		// web上下文,可以在后面的请求链路上通过request.getAttribute取出来使用
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); // 本地化处理器
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  // 主题处理器,2003年的产物
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); // 主题资源文件

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		try {
			//重点关注,核心!!!!!!!!!!!!!!!!!!!!
			doDispatch(request, response);  // ===> 没说的,进!
		} finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

进入核心
4)org.springframework.web.servlet.DispatcherServlet#doDispatch

//	Spring MVC的最核心代码
	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);  // ===>  图例第2-3步
				if (mappedHandler == null) {
					// 如果当前请求没有handler进行处理,那么就抛出异常
					noHandlerFound(processedRequest, response);
					return;
				}

				// handler适配器,找到:RequestMappingHandlerAdapter ,可以通过debug变量查看类型
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  // ===> 图例第4步

				// get方法
				String method = request.getMethod();
				//get方法为true
				boolean isGet = "GET".equals(method);
				//method为get
				if (isGet || "HEAD".equals(method)) { // 处理last-modified , 浏览器缓存时间
					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;
					}
				}
				//循环调拦截器对应的 pre 方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {   // ===> applyPreHandle
					return;
				}

				// 让handlerAdapter去执行业务控制器方法,  真正进入controller的入口!
				// com.spring.mvc.test.MvcController.getModeAndView
				// 让它来返回modelAndView对象! debug仔细查看 mv 变量的值
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  //  ===>  controller入口,走起~

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 设置默认视图,当返回了mav,但是又没设置具体view的时候
				applyDefaultViewName(processedRequest, mv);  // ===>
				mappedHandler.applyPostHandle(processedRequest, response, mv);  // ===> 拦截器后置调用 postHandle
			} 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);
			}
			//1、请求视图解析器,解析成view ,   实例图8-9步
			//2、执行页面渲染(jsp)  ,  示例图 10-11步
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  // ===> go!
		} 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);
				}
			}
		}
	}

getHandler
@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//不止一个,比如BeanNameHandlerMapping、SimpleUrlHandlerMapping,
		// 还有我们需要的RequestHandlerMapping
		if (this.handlerMappings != null) {
			for (HandlerMapping hm : this.handlerMappings) {
				if (logger.isTraceEnabled()) {
					logger.trace(
							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
				}
				//这个就是执行器链,里面包含了需要处理这个请求的controller
				HandlerExecutionChain handler = hm.getHandler(request);  // ===> 【关键点】怎么根据url取到controller的?
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

在这里插入图片描述

@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);  // ===>  找到controller和方法的地方!
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {  // 如果是字符串,从factory取出对应的bean来
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		//所谓的handler链就是这里!其实就是包装一下,加上拦截器列表,详情看 HandlerExecutionChain 的源码结构!
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);  // ===>
		if (CorsUtils.isCorsRequest(request)) { //如果是跨域请求(从header里加了Origin的话)
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config); //就给它加一个拦截器
		}
		return executionChain; // handler链返回给 DS
	}
@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);  // 请求的url值
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock(); //加一把读锁,也就是说mapping是可以被修改的!
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);  // ===> 藏在这里!
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			//return之前处理一下。如果是string的话,找到对应的bean返回去
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); // ===>
		}
		finally {
			this.mappingRegistry.releaseReadLock(); //释放掉!
		}
	}
@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		//定义一个集合,存储所有匹配上的方法
		List<Match> matches = new ArrayList<>();
		// mappingRegistry , 映射关系 component-scan扫进去的!debug一下,留心里面的 mappingLookUp……
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {  // directPathMatches,所有匹配上的,可能有多个符合条件的
			addMatchingMappings(directPathMatches, matches, request);  // 把他们整理一下,塞到matches这个list里
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator); //根据定义的比较器,对多个匹配的method排序
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
						lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0); // 取匹配优先级最高的那个
			if (matches.size() > 1) {
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {  // 如果第1 第 2 两个匹配优先级相同
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");  // 扔出异常,提示重复
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request); // ===> 没干啥事,映射关系在request里设置了个attribute
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

拦截器这一块

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
		//返回handler链,其实就是包装了handler和MappedInterceptor类型的拦截器们到一个对象里
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}
getHandlerAdapter
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

		//初始化9大组件里的 initHandlerAdapters, 本类的onRefresh方法里
		if (this.handlerAdapters != null) {
			for (HandlerAdapter ha : this.handlerAdapters) {
				if (logger.isTraceEnabled()) {
					logger.trace("Testing handler adapter [" + ha + "]");
				}
				if (ha.supports(handler)) {
					return ha;
				}
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

调用业务Controller(核心)

执行我们的业务控制器方法,com.spring.mvc.test.MvcController.getModeAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInterna


@Override
	protected boolean supportsInternal(HandlerMethod handlerMethod) {
		return true;
	}

//	开始调用业务控制器
	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// 开始调用业务控制器!!!!!!!!!!!!!!!!!!!!!
			mav = invokeHandlerMethod(request, response, handlerMethod);  // ==> go!
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				prepareResponse(response);
			}
		}

		return mav;
	}

@Nullable
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		// 巴拉巴拉一堆前期set准备操作,不管他,往下看!
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
			if (this.argumentResolvers != null) {
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
			}
			if (this.returnValueHandlers != null) {
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
			}
			invocableMethod.setDataBinderFactory(binderFactory);
			invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

			ModelAndViewContainer mavContainer = new ModelAndViewContainer();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
			asyncWebRequest.setTimeout(this.asyncRequestTimeout);

			WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
			asyncManager.setTaskExecutor(this.taskExecutor);
			asyncManager.setAsyncWebRequest(asyncWebRequest);
			asyncManager.registerCallableInterceptors(this.callableInterceptors);
			asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

			if (asyncManager.hasConcurrentResult()) {
				Object result = asyncManager.getConcurrentResult();
				mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
				asyncManager.clearConcurrentResult();
				if (logger.isDebugEnabled()) {
					logger.debug("Found concurrent result value [" + result + "]");
				}
				invocableMethod = invocableMethod.wrapConcurrentResult(result);
			}
        //反射调用业务控制器开始!!!【关键点】!!!!!!
			invocableMethod.invokeAndHandle(webRequest, mavContainer);  // ===> 反射调用
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        //反射调用
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);  // ===>
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
			}
			throw ex;
		}
	}
processDispatchResult(视图解析和渲染)

org.springframework.web.servlet.DispatcherServlet#

//1、请求视图解析器,解析成view
	//2、执行页面渲染(jsp)
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
									   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
									   @Nullable Exception exception) throws Exception {

		boolean errorView = false;
		//如果有异常,处理异常,也就是我们平时搞的默认错误页
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			} else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);// 其实就是交给系统默认handler去处理
				errorView = (mv != null);
			}
		}


		if (mv != null && !mv.wasCleared()) {
			//视图解析器+渲染!!!【关键点】
			render(mv, request, response);  // ===> 进去!
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale =
				(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);  // 本地化信息

		View view;
		//viewName=index,也就是controller返回的 mav 里的 路径字符串
		String viewName = mv.getViewName();
		if (viewName != null) {
			// 获取视图解析器、解析成view (jstlview),下面渲染,
			view = resolveViewName(viewName, mv.getModelInternal(), locale, request);   // ===>  准备好了吗?层级很深
			if (view == null) {
				throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
						"' in servlet with name '" + getServletName() + "'");
			}
		} else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			view = mv.getView();
			if (view == null) {
				throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
		}
		try {
			if (mv.getStatus() != null) {
				response.setStatus(mv.getStatus().value());  // 如果有异常码,设置给response
			}
			//执行页面渲染, AbstractView.render 【关键点】
			view.render(mv.getModelInternal(), request, response);  // ===>  图例10-11步,渲染视图,扔给response
		} catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
						getServletName() + "'", ex);
			}
			throw ex;
		}
	}

具体解析

	@Override
	@Nullable
	public View resolveViewName(String viewName, Locale locale) throws Exception {
		// 注意,调试的时候,如果请求过相同的视图,会有缓存,那怎么办呢?
		// 我们给他在最前面加一行,让他永远create,
		// 注意,下面这行是我们为了调试自己加的!原始代码从if开始……

		createView(viewName, locale); // ===>  自己加的,debug 从这里进去

		if (!isCache()) {
			return createView(viewName, locale);
		}
		else {
			Object cacheKey = getCacheKey(viewName, locale);
			View view = this.viewAccessCache.get(cacheKey);
			if (view == null) {
				synchronized (this.viewCreationCache) {
					view = this.viewCreationCache.get(cacheKey);
					if (view == null) {
						// Ask the subclass to create the View object.
						view = createView(viewName, locale);
						if (view == null && this.cacheUnresolved) {
							view = UNRESOLVED_VIEW;
						}
						if (view != null) {
							this.viewAccessCache.put(cacheKey, view);
							this.viewCreationCache.put(cacheKey, view);
							if (logger.isTraceEnabled()) {
								logger.trace("Cached view [" + cacheKey + "]");
							}
						}
					}
				}
			}
			return (view != UNRESOLVED_VIEW ? view : null);
		}
	}

@Override
	protected View createView(String viewName, Locale locale) throws Exception {
		// If this resolver is not supposed to handle the given view,
		// return null to pass on to the next resolver in the chain.
		if (!canHandle(viewName, locale)) {
			return null;
		}
		// Check for special "redirect:" prefix.
		if (viewName.startsWith(REDIRECT_URL_PREFIX)) {  // redirect:开头的路径,进行重定向操作!
			String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
			RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
			String[] hosts = getRedirectHosts();
			if (hosts != null) {
				view.setHosts(hosts);
			} // 设置一堆的重定向信息,url,host等
			return applyLifecycleMethods(viewName, view);
		}
		// Check for special "forward:" prefix.
		if (viewName.startsWith(FORWARD_URL_PREFIX)) {   // 同样,forward: 开头的
			String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
			return new InternalResourceView(forwardUrl);
		}
		// Else fall back to superclass implementation: calling loadView.
		return super.createView(viewName, locale);   // ===>  如果都不是,进行create,这才是常规操作!
	}

进到最后一个create中

	protected AbstractUrlBasedView buildView(String viewName) throws Exception {
		Class<?> viewClass = getViewClass();
		Assert.state(viewClass != null, "No view class");

		AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
		view.setUrl(getPrefix() + viewName + getSuffix());  // 关键点! 把你设置的前缀 + 名字 + 后缀,作为模板路径!

		// 后面一堆的set各种属性……,
		// 不管他!设置完成之后,最下面return回去
		String contentType = getContentType();
		if (contentType != null) {
			view.setContentType(contentType);
		}

		view.setRequestContextAttribute(getRequestContextAttribute());
		view.setAttributesMap(getAttributesMap());

		Boolean exposePathVariables = getExposePathVariables();
		if (exposePathVariables != null) {
			view.setExposePathVariables(exposePathVariables);
		}
		Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
		if (exposeContextBeansAsAttributes != null) {
			view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
		}
		String[] exposedContextBeanNames = getExposedContextBeanNames();
		if (exposedContextBeanNames != null) {
			view.setExposedContextBeanNames(exposedContextBeanNames);
		}

		return view;
	}

接下来是渲染render

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

		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
				" and static attributes " + this.staticAttributes);
		}
		//整理合并模型的数据,收集到一个map里,
		// 断点查看 mergedModel 的值,找到controller里定义的数据~~~
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response); //设置了俩header,不管他
		//执行,重点在这里!【关键点】
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); // ===> 进!
	}

最后一步,转发给给servlet

@Override
	protected void renderMergedOutputModel(
			// 这里把所有的相关信息都收集到一个Map里
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		//把响应数据设置到request对象的attribute中,数据就交给了servlet内置容器
		// debug查看 model 的数据试试!会发现我们绑定过的数据~~~
		exposeModelAsRequestAttributes(model, request);  // ===> 【关键点:数据绑定】
		// Expose helpers as request attributes, if any.
		exposeHelpers(request);  // ===> 就设置了俩header
		//获取到跳转的地址,预处理路径
		// 判断是否跳转路径死循环
		String dispatcherPath = prepareForRendering(request, response);  // ===>

		//servlet api,得到转发路径。
		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
		if (rd == null) {
			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
					"]: Check that the corresponding file exists within your web application archive!");
		}

		// If already included or response already committed, perform include, else forward.
		if (useInclude(request, response)) {
			response.setContentType(getContentType());  // content-type设置
			if (logger.isDebugEnabled()) {
				logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			rd.include(request, response);
		} else {
			// Note: The forwarded resource is supposed to determine the content type itself.
			if (logger.isDebugEnabled()) {
				logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
			}
			//forward转发!
			// 后面的步骤就交给了servlet容器。jsp解析,返回给前台浏览器。
			rd.forward(request, response);
		}
	}

总结

在这里插入图片描述

  1. tomcat接收到请求后发给DispatcherServlet
  2. DispatcherServlet接收到请求后去handle中查询对应的url地址,找到匹配度最高的一条
  3. 组装成执行链路(具体执行的业务和拦截器),返回给DispatcherServlet
  4. 处理器映射器执行拦截器中的pre方法->处理器反射调用业务控制器处理业务->处理器映射器执行拦截器中的postHandle(后处理器),并将结果返回给DispatcherServlet
  5. DispatcherServlet交给视图解析器进行路径拼接和数据绑定,设置响应信息和一些检查(否跳转路径死循环)
  6. 最终forward转发给tomcat,通过tomcat响应给客户端

源码中视图解析和渲染放在一块做了,所以这里写一块了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值