• 爱上源码,重学Spring MVC深入


    1.1 gradle搭建源码调试环境

    1)搭建gradle环境

    4个步骤

    1、File-New-Module

    选择java和web

    file

    2、填写包信息

    file
    3、存储路径

    file

    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"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    3)MVC代码编写

    前提:

    增加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创建完毕
    file

    webapp/WEB-INF/web.xml

    
    <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">
    
    	
    	<servlet>
    		<servlet-name>mvcservlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    		<init-param>
    			<param-name>contextConfigLocationparam-name>
    			<param-value>classpath:mvc-servlet.xmlparam-value>
    			
    		init-param>
    		
    		<load-on-startup>1load-on-startup>
    	servlet>
    	<servlet-mapping>
    		<servlet-name>mvcservlet-name>
    		<url-pattern>/url-pattern>
    	servlet-mapping>
    
    web-app>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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="suffix" value=".jsp"/>
    	bean>
    	
    	<mvc:annotation-driven/>
    	
    	<mvc:default-servlet-handler/>
    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    webapp/index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
      
        SpringMvc源码深入剖析
      
      
      Gradle构建Spring MVC例子....
      
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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() {
    		//创建一个模型视图对象
    		ModelAndView mav = new ModelAndView("index");
    		return mav;
    	}
    	@RequestMapping("/text")
    	@ResponseBody
    	public String text() {
    		return "Text...";
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    4)启动MVC项目

    两种启动方式

    方式一:外挂启动

    idea环境外面启动(项目根目录下运行 gradle + task name)

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

    在项目根目录中执行gradle tomcatRun

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

    控制台正常输出

    file

    方式二:集成到idea中启动

    file

    设置

    file

    即可点击运行

    file

    运行成功

    file

    方式三:

    idea右边找到gradle的task,直接双击,这个爽~

    file

    访问MVC项目

    注意:spring-test-mvc是项目的名称

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

    效果如下

    file

    5)源码调试配置

    idea里的调试

    简单,debug模式启动tomcat即可

    file

    远程调试模式

    重要

    想要远程debug,需要使用上面的方法二,因为debug启动需要设置gradle的环境变量,

    即运行gradle命令的时候插入一些参数命令。

    增加debug参数,对外暴露5005端口,即监听5005端口。

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

    file

    在配置Remote;监听5005端口

    点击+号,创建Remote;默认配置即可

    file
    最后一步

    1、先运行tomcat

    2、再运行remote

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

    打上断点试试!

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

    1.2 MVC工作原理和继承关系

    1)MVC底层工作原理

    **目标:**认识SpringMVC的工作原理(对照源码),如何找到对应的Controller,进行页面渲染的

    步骤:11步

    **源头:**http://localhost:8080/spring-test-mvc/index

    file

    SpringMVC工作原理

    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的继承关系

    tips

    不要求记住

    file
    DispatcherServlet 前端总控制器(webmvc源码)

    FrameworkServlet (webmvc源码)

    HttpServletBean 是的一个简单扩展类((webmvc源码)

    HttpServlet(servlet API , 已经离开了spring mvc的控制范围)

    1.3 Spring MVC源码深入剖析

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

    1.3.1 MVC启动阶段

    注意,这个阶段没法debug,我们从servlet规范去直接看源码

    下面的请求阶段,再详细debug请求链路的完整过程

    web.xml回顾

    
    <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">
    
    	
    	<servlet>
    		<servlet-name>mvcservlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
    		<init-param>
    			<param-name>contextConfigLocationparam-name>
    			<param-value>classpath:mvc-servlet.xmlparam-value>
    			
    		init-param>
    		
    		<load-on-startup>1load-on-startup>
    	servlet>
    	<servlet-mapping>
    		<servlet-name>mvcservlet-name>
    		<url-pattern>/url-pattern>
    	servlet-mapping>
    	
    
    	 
    	
    		
    	
    		
    	
    	
    	
    		
    	
    
    web-app>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    从上面的配置,我们可以看出,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

    启动:servlet规范,init方法被容器调用

    在servlet实例化后,被容器调用一次init方法,所以启动我们找到mvc里的父类从init看起

    file

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

    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);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    1.3.2 MVC请求阶段

    需求:我们在浏览器输入http://localhost:8080/spring-test-mvc/index,背后到底做了哪些事情

    目标:MVC如何通过一个url就能找到我们的controller,并返回数据

    1、断点调试
    2、流程图对照
    3、继承关系对照

    流程图解:

    标准Servlet(回顾tomcat源码里,容器最后调的是wrapper的 service 方法)

    伪代码

    interface Servlet{
    	service()  // 1  , 标准servlet规范的入口
    }
    
    HttpServlet implements Servlet{
    	public service(ServletRequest req, ServletResponse res){
    		//转成 HttpServletRequest
    		protected service(req,res);  // 2
    	}
    	protected service(HttpServletRequest req, HttpServletResponse resp){
    		if(isGet){
    			protected doGet()  // 4
    		}		
    	}
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp);  // 5
    }
    
    //spring mvc
    
    FrameworkServlet extends HttpServlet{
    	@Override
    	service(){
    		super.service();  // 3
    	}
    	
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp){
    		processRequest(request, response){
    			protected doService(request, response); // 6
    		}    
    	}
    }
    
    DispatcherServlet extends FrameWorkServlet{
    	protected doService(request, response);  //  7  , here!
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    代码查找的路径:

    file

    tips:

    spring mvc的 FrameworkServlet ,这是我们源码跟踪的入口

    项目启动

    访问

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

    上图的初始化流程在源码中是怎么流转的呢?

    入口:开启请求的大门

    org.springframework.web.servlet.FrameworkServlet:

    java web标准告诉我们,request的get会交给标准 HttpServlet的doGet方法

    而这个类FrameworkServlet,是HttpServlet的子类,覆盖了上述的doGet,

    所以,请求进入spring的第一入口,就在这里!!!

    1)org.springframework.web.servlet.FrameworkServlet#doGet

    调用到了org.springframework.web.servlet.FrameworkServlet#doGet

    	//get请求调用
    	@Override
    	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    		processRequest(request, response);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    //	重点关注:doService
    	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    			throws ServletException, IOException {
    
    		long startTime = System.currentTimeMillis();
    		Throwable failureCause = null;
    
    		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    		LocaleContext localeContext = buildLocaleContext(request);
    
    		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    
    		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    
    		initContextHolders(request, localeContext, requestAttributes);
    
    		try {
    			//重点查看,跳到DispatcherServlet 类中(子类重写)
    			doService(request, response);
    		} 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);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

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

    	//重写父类
    	//重点关注  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.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    		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);
    				}
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    进入核心

    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.这个Handler其实就是我们的控制器,进入!!!!!
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				// 处理器适配器,9大组件初始化
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// Process last-modified header, if supported by the handler.
    				String method = request.getMethod();
    				//get方法为true
    				boolean isGet = "GET".equals(method);
    				//method为get
    				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;
    				}
    
    				// 执行我们的业务控制器方法,com.spring.mvc.test.MvcController.getModeAndView
    				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);
    				}
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    5)org.springframework.web.servlet.DispatcherServlet#getHandler

    	@Nullable
    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		//不止一个,比如BeanNameHandlerMapping、SimpleUrlHandlerMapping,还有我们需要的RequestHandlerMapping
    		//在9个组件初始化的时候赋值
    		if (this.handlerMappings != null) {
    			for (HandlerMapping hm : this.handlerMappings) {
    				if (logger.isTraceEnabled()) {
    					logger.trace(
    							"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
    				}
    				//这个就是执行器链
    				HandlerExecutionChain handler = hm.getHandler(request);
    				if (handler != null) {
    					return handler;
    				}
    			}
    		}
    		return null;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

    	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		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");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    6) 调用业务Controller

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

    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 {
    			// No synchronization on session demanded at all...
    			mav = invokeHandlerMethod(request, response, handlerMethod);
    		}
    
    		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
    			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
    				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
    			}
    			else {
    				prepareResponse(response);
    			}
    		}
    
    		return mav;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    7)org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    //1、请求视图解析器,解析成view
    	//2、执行页面渲染
    	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);
    				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);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    本文由传智教育博学谷教研团队发布。

    如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

    转载请注明出处!

  • 相关阅读:
    Going Deeper With Convolution(GoogLeNet的理解)
    数学笔记;离散傅里叶变化 DFT
    [Linux]----文件操作(复习C语言+文件描述符)
    深入了解Vue.js框架:构建现代化的用户界面
    编码后的字符串lua
    概率论得学习整理--番外3:二项式定理和 二项式系数
    sqlcoder实践
    AI推介-大语言模型LLMs论文速览(arXiv方向):2024.05.20-2024.05.25
    生成一篇博客,详细讲解springboot的单点登录功能,有流程图,有源码demo
    VUE+element可以为空不为空时只能为(正整数和0)的验证
  • 原文地址:https://blog.csdn.net/bxg_kyjgs/article/details/127650395