DispatcherServlet 源码解析

2/17/2022 JavaSpring

摘要

JDK:1.8.0_202
Spring Version:5.2.11.RELEASE

# 一:前言

本文将分析 SpringMVC 的核心分发器 DispatcherServlet 的初始化过程以及处理请求的过程,了解这个入口Servlet的作用。

# 二:继承关系

在分析DispatcherServlet之前,我们先看下DispatcherServlet的继承关系。

继承关系

继承关系

# 三:HttpServletBean

HttpServletBean 重写了 GenericServlet 的 init() 方法。对初始化过程做了一些处理。

GenericServlet.java

package javax.servlet;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    public void init() throws ServletException {
    }
}
1
2
3
4
5
6

HttpServletBean.java





































 












package org.springframework.web.servlet;

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

	/**
	 * 将配置参数映射到此servlet的bean属性上,并调用子类初始化。
	 * @throws ServletException —— 如果bean属性无效(或缺少必需的属性),或子类初始化失败
	 */
	@Override
	public final void init() throws ServletException {

		// Set bean properties from init parameters.(从init参数设置bean属性)
		// ServletConfigPropertyValues 是 HttpServletBean内部静态类,构成过程中会使用
		// ServletConfig 对象找出 web.xml 配置文件中的配置参数并设置 ServletConfigPropertyValues
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				// 使用BeanWrapper构造DispatcherServlet
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				// Resource 类型参数使用属性编辑器
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				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;
			}
		}

		// Let subclasses do whatever initialization they like.(让子类做他们喜欢的任何初始化)
		// 默认实现不做任何处理,子类覆盖该方法可做任何处理,也就是初始化的时候做更多的事情
		initServletBean();
	}
	
	/**
	 * 子类可能会覆盖它来执行自定义初始化。
	 * 在调用此方法之前,将设置此servlet的所有bean属性。
	 * 此默认实现为空。
	 * @throws ServletException–如果子类初始化失败
	 */
	protected void initServletBean() throws ServletException {
	}
}
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

web.xml(配置文件例子)

<!-- spring mvc servlet-->
<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>SpringMVC</servlet-name>
    <!-- 此处也可以配置成 *.do 形式 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

比如上面这段配置,传递了contextConfigLocation参数,之后构造BeanWrapper,这里使用BeanWrapper 有2个理由:

  1. contextConfigLocation 属性在 FrameworkServlet 中定义,HttpServletBean中未定义

  2. 利用 Spring 的注入特性,只需要调用 setPropertyValues 方法就可将 contextConfigLocation 属性设置到对应实例中,也就是以依赖注入的方式初始化属性。然后设置 DispatcherServlet 中的 contextConfigLocation 属性(FrameworkServlet中定义)为 web.xml 中读取的contextConfigLocation参数,该参数用于构造SpringMVC容器上下文。

# 四:FrameworkServlet

上面说到子类重写 HttpServletBean#initServletBean() 达到初始化自定义处理。

HttpServletBean



























 













































































 
















package org.springframework.web.servlet;

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {

	/**
	 * 重写HttpServletBean的方法
	 * 设置完若干bean属性后调用
	 * 创建这个servlet的是WebApplicationContext
	 */
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		long startTime = System.currentTimeMillis();

		try {
			// 初始化 WebApplicationContext 属性
			// WebApplicationContext 是继承自 ApplicationContext接口
			// 该属性也就是Spring容器上下文
			// FrameworkServlet的作用就是将Servlet与Spring容器关联
			this.webApplicationContext = initWebApplicationContext();
			// 未做任何处理
			// 该方法主要是为了让子类重写该方法并做一些需要的处理
			// 在 DispatcherServlet 重写该方法
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}

		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}

		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}
	
	/**
	 * 此servlet初始化并发布WebApplicationContext
	 * 委托createWebApplicationContext()方法以实际创建上下文
	 * 可以在子类中重写
	 * @return:WebApplicationContext实例
	 */
	protected WebApplicationContext initWebApplicationContext() {
		// 得到根上下文
		WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		// DispatcherServlet有个以WebApplicationContext为参数的构造函数
        // 当使用有WebApplicationContext参数的构造函数去构造时使用这段代码
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it(在构建时注入了一个上下文实例->使用它)
			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(设置父上下文、设置应用程序上下文id等)
					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);
				}
			}
		}
        // 以contextAttribute 属性(FrameworkServlet的String类型属性)为key
        // 从ServletContext中找到WebApplicationContext
        // 一般不会设置contextAttribute属性,也就是说这里找到的wac为null
		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(已在servlet上下文中注册。如果存在,则假定存在)
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
            // (已设置父上下文(如果有),并且已设置父上下文(如果有),并且用户已执行任何初始化,例如设置上下文id)
			wac = findWebApplicationContext();
		}
        // 创建 WebApplicationContext 并设置根上下文为父上下文
        // 然后配置ServletConfig
        // ServletContext等实例到这个上下文中
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
            // 没有为此 servlet 定义上下文实例 -> 创建一个本地实例
			wac = createWebApplicationContext(rootContext);
		}
        // 模板方法,WebApplicationContext创建成功之后会进行调用
        // FrameworkServlet空实现
        // 子类Dispatcher会重写onRefresh()这个方法
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh(上下文不是带有刷新的ConfigurableApplicationContext)
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
            // 在构建时支持注入已刷新的上下文 -> 这里手动触发初始onRefresh
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
            // 将上下文发布到 servlet 上下文属性。
			String attrName = getServletContextAttributeName();
            // 将新创建的容器上下文设置到ServletContext中
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

}
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

这里的根上下文是根据 web.xml 中配置的 ContextLoaderListener 监听器

<!-- spring监听器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- spring mvc servlet-->
<servlet>
	<servlet-name>SpringMVC</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
	</init-param>
</servlet>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

比如这段配置文件中根据 classpath:spring-mvc.xml 下的 xml 文件生成的根上下文

# 五:DispatcherServlet

DispatcherServlet 重写了 FrameworkServlet 中的 onRefresh() 方法:

package org.springframework.web.servlet;

public class DispatcherServlet extends FrameworkServlet {

	/**
	 * 此实现调用 initStrategies
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		// 初始化各种策略接口的实现类
		initStrategies(context);
	}
	
	/**
	 * 初始化此 servlet 使用的策略对象
	 * 可以在子类中重写,以初始化进一步的策略对象。
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		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

总结一下各个Servlet的作用:

  1. HttpServletBean

主要做一些初始化的工作,将 web.xml 中配置的参数设置到 Servlet 中。比如servlet标签的子标签init-param标签中配置的参数。

  1. FrameworkServlet

将 Servlet 与 Spring 容器上下文关联。其实也就是初始化 FrameworkServlet 的属性 webApplicationContext,这个属性代表SpringMVC上下文,它有个父类上下文,即 web.xml 中配置的 ContextLoaderListener 监听器初始化的容器上下文。

  1. DispatcherServlet

初始化各个功能的实现类。比如异常处理、视图处理、请求映射处理等。

# 六:DispatcherServlet处理请求过程

在分析DispatcherServlet处理请求过程之前,先回顾一下Servlet对于请求的处理

HttpServlet 提供了 service() 方法用于处理请求,service() 使用了模板设计模式,在内部对于 HTTP get 请求会调用 doGet() 方法,HTTP post 请求会调用 doPost() 方法,以此类推

HttpServlet.java







































































































































 




package javax.servlet.http;

public abstract class HttpServlet extends GenericServlet {

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_get_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
            doGet(req, resp);
        } else {
            NoBodyResponse response = new NoBodyResponse(resp);
            doGet(req, response);
            response.setContentLength();
        }
    }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_put_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }

	private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
        String protocol = req.getProtocol();
        // Note: Tomcat reports "" for HTTP/0.9 although some implementations
        //       may report HTTP/0.9
        if (protocol.length() == 0 || protocol.endsWith("0.9") || protocol.endsWith("1.0")) {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        }
    }

	/**
	 * 接收标准HTTP请求, 并将它们分派到此类中定义的doMethod()方法
	 * 此方法是javax.servlet.Servlet.service()方法的具体实现
	 * 无需重写此方法
	 */
	protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        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
                // (servlet不支持if-modified-since)
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // 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);
        }
    }
    
    /**
     * 实现Servlet接口
     * 将客户端请求分派到受保护的服务方法
     * 无需重写此方法
     * @param HttpServletRequest对象,其中包含客户端对servlet发出的请求
     * @param HttpServletResponse对象,其中包含servlet返回给客户端的响应
     * @exception IOException 如果在 servlet 处理 HTTP 请求时发生输入或输出错误
     * @exception ServletException 如果无法处理HTTP请求
     */
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        service(request, response);
    }

}
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

FrameworkServlet.java

FrameworkServlet 重写了 HttpServlet 的 doGet()、doPost() 等方法










































 





















package org.springframework.web.servlet;

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
	/*
	 * 将 GET 请求委托给 processRequest/doService
	 */
@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}
	
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}
	
	/**
	 * 处理此请求,无论结果如何都发布事件
	 * 实际的事件处理由抽象的 doService 模板方法执行
	 * processRequest()方法只是做了一些线程安全的隔离,真正的请求处理,发生在doService()方法中
	 */
	protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
		
		// 得到与当前请求线程绑定的LocalContext和ServletRequestAttributes对象
		// 然后构造新的Locale和ServletRequestAttributes对象
		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());

		// 让新构造的LocaleContext和RequestAttributes与当前请求线程绑定(通过ThreadLocal完成)
		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 {
			// doService()方法执行完成之后重置LocaleContext与RequestAttributes对象
			// 重置也就是解除请求线程与LocaleContext和RequestAttributes对象的绑定
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			// 执行成功之后,发布ServletRequestHandledEvent事件
			// 可以通过注册监听器来监听该事件的发布
			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
49
50
51
52
53
54
55
56
57
58
59
60
61
62

其中注册的监听器类型为ApplicationListener接口类型

DispatcherServlet.java
































 
















 

















































































































package org.springframework.web.servlet;

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		// 如果该请求是include请求,那么保存一份快照版本的request域中的数据
		// doDispatcher() 方法结束之后,这个快照版本中的数量将会覆盖新的request域中的数据
		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域中的一些属性
		// 几个requet.setAttribute()方法的调用,将前面在初始化流程中实例化的对象设置到http请求的属性中,供下一步处理使用
		// 其中有容器的上下文对象、本地化解析器等SpringMVC特有的编程元素
		// 不同于Struts2中的ValueStack,SpringMVC的数据并没有从HttpServletRequest对象中抽离出来再存进另外一个编程元素
		// 这也跟SpringMVC的设计思想有关
		// 因为从一开始,SpringMVC的设计者就认为,不应该将请求处理过程和Web容器完全隔离
		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()方法进行请求分发处理
			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);
				}
			}
		}
	}
	
    /**
     * 处理程序的实际调度
     * 将通过依次应用 servlet 的 HandlerMappings 来获取处理程序
     * HandlerAdapter 将通过查询 servlet 已安装的 HandlerAdapter 来找到第一个支持该处理程序类的
     * 所有 HTTP 方法都由该方法处理。 由 HandlerAdapters 或处理程序自己决定哪些方法是可接受的
     * 它的参数是HttpServletRequest和HttpServletResponse对象。
     * 这给我们传递的意思也很明确,从request中能获取到一切请求的数据,从response中,我们又可以往服务器端输出任何响应
     * Http请求的处理,就应该围绕这两个对象来设计
     * 我们不妨可以将SpringMVC这种设计方案,是从Struts2的过度设计中吸取教训,而向Servlet编程的一种回归和简化。
     */
	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);

				// Determine handler for the current request.
				// 决定处理当前请求的handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 决定当前请求的HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				// 处理last-modified请求头
				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;
					}
				}

				// 拦截器的前置处理
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// handler实际执行请求
				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,
				// (从4.3开始,我们也从处理程序方法中抛出错误,)
				// making them available for @ExceptionHandler methods and other scenarios.
				// (使它们可用于@ExceptionHandler方法和其他场景。)
				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
                // 代替 postHandle 和 afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
                // 清除一些 multipart 请求的资源
				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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

doDispatch方法功能简单描述:

  1. 首先根据请求的路径找到 HandlerMethod(带有Method反射属性,也就是对应Controller中的方法)
  2. 然后匹配路径对应的拦截器
  3. 有了HandlerMethod和拦截器构造个HandlerExecutionChain对象
  4. HandlerExecutionChain对象的获取是通过HandlerMapping接口提供的方法中得到
  5. 有了HandlerExecutionChain之后,通过HandlerAdapter对象进行处理得到ModelAndView对象,HandlerMethod内部handle的时候,使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数
  6. 使用各种HandlerMethodReturnValueHandler实现类处理返回值
  7. 最终返回值被处理成ModelAndView对象,这期间发生的异常会被HandlerExceptionResolver接口实现类进行处理

# 七:参考文献

最后更新: 2/27/2022, 8:30:40 PM