1. Java SpringMVC源码分析

目录

  • [1. 使用](#1. 使用)
    • [1.1. 新建项目](#1.1. 新建项目)
      • [1.1.1. 第一种方式](#1.1.1. 第一种方式)
        • [1.1.1.1. 新建web项目](#1.1.1.1. 新建web项目)
        • [1.1.1.2. 导入jar包依赖](#1.1.1.2. 导入jar包依赖)
      • [1.1.2. 第二种方式](#1.1.2. 第二种方式)
        • [1.1.2.1. 新建maven工程](#1.1.2.1. 新建maven工程)
        • [1.1.2.2. 添加web框架支持](#1.1.2.2. 添加web框架支持)
        • [1.1.2.3. maven pom 依赖](#1.1.2.3. maven pom 依赖)
        • [1.1.2.4. 修改project配置lib包](#1.1.2.4. 修改project配置lib包)
    • [1.2. 配置spring mvc](#1.2. 配置spring mvc)
    • [1.3. 新建controller](#1.3. 新建controller)
    • [1.4. 新建jsp](#1.4. 新建jsp)
    • [1.5. 新建html文件](#1.5. 新建html文件)
    • [1.6. 配置tomcat](#1.6. 配置tomcat)
    • [1.7. 项目结构](#1.7. 项目结构)
    • [1.8. 启动并访问](#1.8. 启动并访问)
  • [2. 源码分析](#2. 源码分析)
    • [2.1. 打断点](#2.1. 打断点)
    • [2.2. servlet类图](#2.2. servlet类图)
    • [2.3. DispatcherServlet是怎么加载的](#2.3. DispatcherServlet是怎么加载的)
    • [2.4. 关键方法是哪个](#2.4. 关键方法是哪个)
      • [2.4.1. HttpServlet service](#2.4.1. HttpServlet service)
      • [2.4.2. FrameworkServlet service](#2.4.2. FrameworkServlet service)
      • [2.4.3. HttpServlet 另一个service](#2.4.3. HttpServlet 另一个service)
      • [2.4.4. FrameworkServlet doGet](#2.4.4. FrameworkServlet doGet)
      • [2.4.5. FrameworkServlet processRequest](#2.4.5. FrameworkServlet processRequest)
      • [2.4.6. DispatcherServlet doService](#2.4.6. DispatcherServlet doService)
      • [2.4.7. DispatcherServlet doDispatch【关键】](#2.4.7. DispatcherServlet doDispatch【关键】)
    • [2.5. Handler是怎么找到的](#2.5. Handler是怎么找到的)
      • [2.5.1. 获取所有HandlerMapping一个个试](#2.5.1. 获取所有HandlerMapping一个个试)
        • [2.5.1.1. HandlerMapping类型](#2.5.1.1. HandlerMapping类型)
          • [2.5.1.1.1. 如果是xml配置或者implements Controller的那么使用BeanNameUrlHandlerMapping](#2.5.1.1.1. 如果是xml配置或者implements Controller的那么使用BeanNameUrlHandlerMapping)
          • [2.5.1.1.2. 如果是注解@Controller配置的那么使用RequestMappingHandlerMapping](#2.5.1.1.2. 如果是注解@Controller配置的那么使用RequestMappingHandlerMapping)
          • [2.5.1.1.3. 如果是静态资源映射且开启了mvc:default-servlet-handler/,使用的是SimpleUrlHandlerMapping](#2.5.1.1.3. 如果是静态资源映射且开启了mvc:default-servlet-handler/,使用的是SimpleUrlHandlerMapping)
        • [2.5.1.2. 通过HandlerMapping获取Handler和拦截器链](#2.5.1.2. 通过HandlerMapping获取Handler和拦截器链)
          • [2.5.1.2.1. 拿到url对应的Handler](#2.5.1.2.1. 拿到url对应的Handler)
    • [2.6. Handler Adapter是怎么适配的](#2.6. Handler Adapter是怎么适配的)
      • [2.6.1. 遍历所有HandlerMappingAdapter一个个试](#2.6.1. 遍历所有HandlerMappingAdapter一个个试)
        • [2.6.1.1. HandlerAdapter类型](#2.6.1.1. HandlerAdapter类型)
          • [2.6.1.1.1. 如果是静态资源或者implements Controller使用的是SimpleControllerHandlerAdapter](#2.6.1.1.1. 如果是静态资源或者implements Controller使用的是SimpleControllerHandlerAdapter)
          • [2.6.1.1.2. 如果是implements HttpRequestHandler那么HttpRequestHandlerAdapter](#2.6.1.1.2. 如果是implements HttpRequestHandler那么HttpRequestHandlerAdapter)
          • [2.6.1.1.3. 如果是Servlet那么SimpleServletHandlerAdapter](#2.6.1.1.3. 如果是Servlet那么SimpleServletHandlerAdapter)
          • [2.6.1.1.4. 如果是注解@Controller配置的那么使用RequestMappingHandlerMappingAdapter](#2.6.1.1.4. 如果是注解@Controller配置的那么使用RequestMappingHandlerMappingAdapter)
    • [2.7. handler的方法是怎么调用的](#2.7. handler的方法是怎么调用的)
      • [2.7.1. 如果是implements Controller的强转直接调用](#2.7.1. 如果是implements Controller的强转直接调用)
      • [2.7.2. @Controller的反射调用](#2.7.2. @Controller的反射调用)
    • [2.8. 视图是怎么渲染的](#2.8. 视图是怎么渲染的)
      • [2.8.1. 获取合适的view并渲染](#2.8.1. 获取合适的view并渲染)
  • [3. 参考](#3. 参考)

1. 使用

1.1. 新建项目

1.1.1. 第一种方式

1.1.1.1. 新建web项目

1.1.1.2. 导入jar包依赖

在WEB-INF下新建lib目录,导入如下依赖。并且add as library

1.1.2. 第二种方式

1.1.2.1. 新建maven工程

1.1.2.2. 添加web框架支持

1.1.2.3. maven pom 依赖

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zsk</groupId>
    <artifactId>springmvc_test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
            </plugin>
        </plugins>
        <!--配置资源目录-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
            <!-- 打包时将jsp文件拷贝到META-INF目录下 -->
            <resource>
                <!-- 指定resources插件处理哪个目录下的资源文件 -->
                <directory>src/main/webapp</directory>
                <!--注意此次必须要放在此目录下才能被访问到 -->
                <targetPath>META-INF/resources</targetPath>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

</project>

1.1.2.4. 修改project配置lib包

1.2. 配置spring mvc

  • WEB-INF/web.xml
    配置DispatcherServlet和springmvc.xml
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">
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • springmvc.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.zsk.controller"/>

    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--寻找RequestMapping-->
    <mvc:annotation-driven/>
    <!--寻找静态资源-->
    <mvc:default-servlet-handler/>
</beans>

1.3. 新建controller

  • com.zsk.controller.TestController
java 复制代码
package com.zsk.controller;

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

/**
 * @description:
 * @author: zsk
 * @create: 2019-11-27 23:43
 **/
@Controller
public class TestController
{
    @RequestMapping("/test")
    public String test()
    {
        return "test";
    }
}
  • com.zsk.controller.TestController2
java 复制代码
package com.zsk.controller;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

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

/**
 * @description:
 * @author: zsk
 * @create: 2019-11-30 21:28
 **/
@Component("/test2")
public class TestController2 implements Controller
{
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception
    {
        ModelAndView test2 = new ModelAndView("test2");
        return test2;
    }
}

1.4. 新建jsp

  • WEB-INF/jsp/test.jsp
jsp 复制代码
<%--
   
  User: zsk
  Date: 2019/11/29
  Time: 20:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Test</title>
  </head>
  <body>
  Hello World
  </body>
</html>
  • WEB-INF/jsp/test2.jsp
jsp 复制代码
<%--
   
  User: zsk
  Date: 2019/11/27
  Time: 23:24
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>Test2</title>
  </head>
  <body>
  Hello2
  </body>
</html>

1.5. 新建html文件

  • web/abc.html
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>abc</title>
</head>
<body>
abc
</body>
</html>

1.6. 配置tomcat

1.7. 项目结构

  • 第一种
  • 第二种

1.8. 启动并访问

2. 源码分析

2.1. 打断点

我们在com.zsk.controller.TestController#test的方法上打上断点,查看调用栈如下

java 复制代码
test:17, TestController (com.zsk.controller)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
doInvoke:190, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:138, InvocableHandlerMethod (org.springframework.web.method.support)
invokeAndHandle:104, ServletInvocableHandlerMethod (org.springframework.web.servlet.mvc.method.annotation)
invokeHandlerMethod:892, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handleInternal:797, RequestMappingHandlerAdapter (org.springframework.web.servlet.mvc.method.annotation)
handle:87, AbstractHandlerMethodAdapter (org.springframework.web.servlet.mvc.method)

doDispatch:1039, DispatcherServlet (org.springframework.web.servlet)
doService:942, DispatcherServlet (org.springframework.web.servlet)
processRequest:1005, FrameworkServlet (org.springframework.web.servlet)
doGet:897, FrameworkServlet (org.springframework.web.servlet)
service:634, HttpServlet (javax.servlet.http)
service:882, FrameworkServlet (org.springframework.web.servlet)
service:741, HttpServlet (javax.servlet.http)

internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:52, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:199, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:528, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:139, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:678, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
service:343, CoyoteAdapter (org.apache.catalina.connector)
service:609, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:810, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1506, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

从中间一段调用栈看出主要经过HttpServletFrameworkServletDispatcherServlet这几个类的方法,我们从搞清楚这几个类的关系

2.2. servlet类图

由图可以看出这几类都是servlet,我们记得servlet的生命周期Servlet.md

所以从HttpServlet的service方法开始追踪源码

2.3. DispatcherServlet是怎么加载的

  • 如果是web.xml配置的

    这是我们自己在web.xml中配置的,在tomcat启动的时候会加载<load-on-startup>1</load-on-startup>的Servlet,同时传入<init-param></init-param>的参数

  • 如果是servlet3.0的配置方式

2.4. 关键方法是哪个

2.4.1. HttpServlet service

java 复制代码
@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }

    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;

	//只是将request、resposne对象封装成HttpServletRequest和HttpServletResponse
	//调用子类的方法
	//org.springframework.web.servlet.FrameworkServlet#service
    service(request, response);
}

2.4.2. FrameworkServlet service

java 复制代码
protected void service(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
	if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
		processRequest(request, response);
	}
	else {
		//调用父类的另一个service方法
		//javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
		super.service(request, response);
	}

}

2.4.3. HttpServlet 另一个service

java 复制代码
 protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String method = req.getMethod();

	//根据http方法调用doXXX方法
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            //get方法
        	//org.springframework.web.servlet.FrameworkServlet#doGet
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
        
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
        
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
        
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
        
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
        
    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

2.4.4. FrameworkServlet doGet

java 复制代码
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
	//调用FrameworkServlet#processRequest
	processRequest(request, response);
}

2.4.5. FrameworkServlet processRequest

java 复制代码
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

//...

	try {
	//调用DispatcherServlet#doService
		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();
		}
		logResult(request, response, failureCause, asyncManager);
		publishRequestHandledEvent(request, response, startTime, failureCause);
	}
}

2.4.6. DispatcherServlet doService

java 复制代码
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...
	try {
	    //调用DispatcherServlet#doDispatch
		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);
			}
		}
	}
}

2.4.7. DispatcherServlet doDispatch【关键】

java 复制代码
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);


				//1.获取相应的HandlerExecutionChain【包括handler和interceptor】处理请求
				//handler是我们自己的处理类
				//intercepter是拦截器
				mappedHandler = getHandler(processedRequest);
				//没找到handler,那么控制台打印not found,跳转到404
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}


				//2.将Handler封装成HandlerAdapter【adpater的作用是适配不同的handler
				//比如有@Controller、implement Controller、implement RequestHandler等】
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());


				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//3.调用interceptor的preHandle处理
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//4.真正调用handler的方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				//5.调用interceptor的postHandle处理
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			//6.渲染视图
			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()) {
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

2.5. Handler是怎么找到的

2.5.1. 获取所有HandlerMapping一个个试

  • getHandler
java 复制代码
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//查找合适的HandlerExecutionChain【包括handler和interceptor】
	if (this.handlerMappings != null) {
		//不同的Handler有不同的HandlerMapping处理
			//如果是xml配置或者implements Controller的那么使用BeanNameUrlHandlerMapping
			//如果是注解@Controller配置的那么使用RequestMappingHandlerMapping
			//如果是静态资源映射且开启了<mvc:default-servlet-handler/>,使用的是SimpleUrlHandlerMapping
		//HandlerMapping已经提前定义好了:C:/Users/zsk/.m2/repository/org/springframework/spring-webmvc/5.1.8.RELEASE/spring-webmvc-5.1.8.RELEASE.jar!/org/springframework/web/servlet/DispatcherServlet.properties
		for (HandlerMappig mapping : this.handlerMappings) {
			//只要找到一个能处理的就返回
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

2.5.1.1. HandlerMapping类型

2.5.1.1.1. 如果是xml配置或者implements Controller的那么使用BeanNameUrlHandlerMapping
2.5.1.1.2. 如果是注解@Controller配置的那么使用RequestMappingHandlerMapping
2.5.1.1.3. 如果是静态资源映射且开启了mvc:default-servlet-handler/,使用的是SimpleUrlHandlerMapping

2.5.1.2. 通过HandlerMapping获取Handler和拦截器链

  • AbstractHandlerMapping#getHandler
java 复制代码
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	Object handler = getHandlerInternal(request);

	//尝试静态资源
	if (handler == null) {
		handler = getDefaultHandler();
	}
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

	//加上拦截器
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

	if (logger.isTraceEnabled()) {
		logger.trace("Mapped to " + handler);
	}
	else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
		logger.debug("Mapped to " + executionChain.getHandler());
	}

	if (CorsUtils.isCorsRequest(request)) {
		CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

	return executionChain;
}
2.5.1.2.1. 拿到url对应的Handler
  • AbstractHandlerMethodMapping#getHandlerInternal
java 复制代码
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//拿到url路径
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	this.mappingRegistry.acquireReadLock();
	try {
		//通过url路径从map中查找
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

2.6. Handler Adapter是怎么适配的

2.6.1. 遍历所有HandlerMappingAdapter一个个试

  • getHandlerAdapter
java 复制代码
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	//如果是注解@Controller配置的那么使用RequestMappingHandlerMappingAdapter【AbstractHandlerMethodAdapter】
	//如果是静态资源或者implements Controller使用的是SimpleControllerHandlerAdapter
	//如果是implements HttpRequestHandler那么HttpRequestHandlerAdapter
	//如果是Servlet那么SimpleServletHandlerAdapter
	if (this.handlerAdapters != null) {
		for (HandlerAdapter adapter : this.handlerAdapters) {
			//适配器是否匹配当前handler
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

2.6.1.1. HandlerAdapter类型

2.6.1.1.1. 如果是静态资源或者implements Controller使用的是SimpleControllerHandlerAdapter
  • SimpleControllerHandlerAdapter#supports
java 复制代码
public boolean supports(Object handler) {
	return (handler instanceof Controller);
}
2.6.1.1.2. 如果是implements HttpRequestHandler那么HttpRequestHandlerAdapter
  • HttpRequestHandlerAdapter#supports
java 复制代码
public boolean supports(Object handler) {
	return (handler instanceof HttpRequestHandler);
}
2.6.1.1.3. 如果是Servlet那么SimpleServletHandlerAdapter
  • SimpleServletHandlerAdapter#supports
java 复制代码
public boolean supports(Object handler) {
	return (handler instanceof Servlet);
}
2.6.1.1.4. 如果是注解@Controller配置的那么使用RequestMappingHandlerMappingAdapter
  • AbstractHandlerMethodAdapter#supports
java 复制代码
public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

2.7. handler的方法是怎么调用的

2.7.1. 如果是implements Controller的强转直接调用

java 复制代码
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
	//强转直接调用
	return ((Controller) handler).handleRequest(request, response);
}

2.7.2. @Controller的反射调用

java 复制代码
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}

protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	//这个
	mav = invokeHandlerMethod(request, response, handlerMethod);
	return mav;
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//这个
	invocableMethod.invokeAndHandle(webRequest, mavContainer);
}


public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	//这个
	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
}

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {
	//解析调用参数
	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
	if (logger.isTraceEnabled()) {
		logger.trace("Arguments: " + Arrays.toString(args));
	}
	//反射调用
	return doInvoke(args);
}

2.8. 视图是怎么渲染的

  • processDispatchResult
java 复制代码
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);
		}
	}

	// Did the handler return a view to render?
	if (mv != null && !mv.wasCleared()) {
		//渲染handler返回的ModelAndView
		render(mv, request, response);
		if (errorView) {
			WebUtils.clearErrorRequestAttributes(request);
		}
	}
	else {
		if (logger.isTraceEnabled()) {
			logger.trace("No view rendering, null ModelAndView returned.");
		}
	}

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

	if (mappedHandler != null) {
		mappedHandler.triggerAfterCompletion(request, response, null);
	}
}

2.8.1. 获取合适的view并渲染

  • DispatcherServlet#render
java 复制代码
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;
	String viewName = mv.getViewName();
	if (viewName != null) {
		// We need to resolve the view name.
		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.
		//通过视图名和ViewResolver获取相应的view
		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.isTraceEnabled()) {
		logger.trace("Rendering view [" + view + "] ");
	}
	try {
		if (mv.getStatus() != null) {
			response.setStatus(mv.getStatus().value());
		}
		//使用view渲染
		view.render(mv.getModelInternal(), request, response);
	}
	catch (Exception ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Error rendering view [" + view + "]", ex);
		}
		throw ex;
	}
}

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;
}

3. 参考