《spring源码深度解析》spring Mvc

———— 5.1.3.RELEASE
words: 5.3k    views:    time: 27min

对于web应用,spring提供了springmvc。其基于Servlet实现,通过DispatchServlet来封装核心功能,对请求进行分派,并提供可配的处理程序映射、试图解析等。

示例

下面通过springmvc搭建一个简单的web应用程序,只需引入spring-webmvc依赖即可

pom.xml
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
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- springmvc默认没有对象转换成json的转换器,需要手动添加jackson依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
<!-- 添加LogbackConfigListener,加载logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.logback-extensions</groupId>
<artifactId>logback-ext-spring</artifactId>
<version>0.1.2</version>
</dependency>
<!-- 编译引用HttpServletRequest -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>

web应用本身并不是必须要有web.xml,但是springmvc是通过servlet实现的,它是必须的,主要有如下两个配置:

  • ContextLoaderListener:主要是将spring应用上下文的加载放到了web容器的启动过程中。每一个web应用都有一个ServletContext与之相关联,其在应用启动时创建,关闭时销毁,并在全局范围内有效,以及在整个运行期间都是可见的。这里主要的实现就是初始化一个WebApplicationContext并放入ServletContext中。

  • DispatcherServlet:封装springmvc的处理逻辑,即拦截web请求,并进行分派处理。

WEB-INF/web.xml
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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">

<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>WEB-INF/logback.xml</param-value>
</context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>

<!-- <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param> -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/springmvc-servlet.xml</param-value>
</init-param> -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

类似之前手动进行初始化ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");,只不过这里在web初始化过程中自动帮我们做了

WEB-INF/applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:component-scan base-package="mvc.test">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>

这里配置springmvc的一些组件,比如处理器,视图解析器,对于Controller既可以自己手动继承实现,也可以通过注解扫描的方式

WEB-INF/springmvc-servlet.xml
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
<?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: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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 注解驱动方式,自动扫描@Controller -->
<mvc:annotation-driven />
<context:component-scan base-package="mvc.test">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

<!-- 手动实现controller方式,配置url映射 -->
<bean id="userController" class="mvc.test.controller.UserController" />

<bean id="urlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/user.view">userController</prop>
</props>
</property>
</bean>

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

手动继承AbstractController实现的话要求返回一个视图ModelAndView

mvc.test.controller.UserController
1
2
3
4
5
6
7
public class UserController extends AbstractController {

@Override
protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) throws Exception {
return new ModelAndView("user", "user", new User("shanhm", "shanhm1991@163.com"));
}
}

比较常用的是通过注解,而且实际使用时更多的是Ajax请求,这种情况下直接定义@ResponseBody返回json数据

mvc.test.controller.TestController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
@RequestMapping("/test")
public class TestController {

@Autowired
private TestService service;

@RequestMapping("/user")
@ResponseBody
public User getUser(){
return service.getUser();
}

@RequestMapping("/user/view")
public ModelAndView viewUser(){
return new ModelAndView("user", "user", service.getUser());
}
}
mvc.test.service.TestService
1
2
3
4
public interface TestService {

User getUser();
}
mvc.test.service.TestServiceImpl
1
2
3
4
5
6
7
8
@Service
public class TestServiceImpl implements TestService {

@Override
public User getUser() {
return new User("shanhm", "shanhm1991@163.com");
}
}
WEB-INF/view/user.jsp
1
2
3
4
5
6
7
8
9
10
11
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>user</title>
</head>
<body>
${user.name} ${user.email}
</body>
</html>

实现

ContextLoaderListener

这里主要就是创建了一个WebApplicationContext实例,并记入ServletContext中,其在ApplicationContext的基础上又增加了一些特定于web的操作和属性。

org.springframework.web.context.ContextLoaderListener
1
2
3
4
5
6
7
8
9
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}

@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
org.springframework.web.context.ContextLoader
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
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 只允许存在一个实例
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}

servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();

try {
// 创建实例
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}

if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) { // 如果context还没有执行refresh(),尝试使用parentContext作为root
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext); // refresh()
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}

if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}

// 这里就是一个反射创建实例
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
//WebApplicationContext的具体实现类型可以通过servlet的参数contextClass指定
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);
}
}else {
//或者在当前类路径下的ContextLoader.properties中指定,具体可以看静态域中的逻辑
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}catch (ClassNotFoundException ex) {
throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

private static final Properties defaultStrategies;

static {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}

DispatcherServlet

DispatcherServletServlet的实现,而Servlet是java服务用来处理HTTP协议请求的一个规范,其生命周期交由servlet容器来控制,比如Tomcat,主要分为3个阶段,即初始化、运行和销毁。

  • 初始化:即由servlet容器创建servlet对象,并调用其init方法

  • 运行:servlet容器针对收到的请求创建servletRequestservletResponse,并作为参数传给service方法来处理

  • 销毁:当web应用终止时,由servlet容器负责调用destory方法,然后在销毁servlet对象

初始化
org.springframework.web.servlet.HttpServletBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final void init() throws ServletException {
// 解析init-parm并封装到pvs中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 将当前servlet类转换为BeanWrapper,从而能以spring的方式对init-parm值进行注入
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true); // 属性注入
}catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
initServletBean();
}
org.springframework.web.servlet.FrameworkServlet
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
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 {
this.webApplicationContext = initWebApplicationContext();
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");
}
}

protected WebApplicationContext initWebApplicationContext() {
// rootContext是前面ContextLoaderListener中创建保存的,将作为下面springmvc容器wac的父容器
WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;

if (this.webApplicationContext != null) {
wac = this.webApplicationContext; // webApplicationContext 作为参数传入的
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext(); // 尝试从ServletContext中获取
}
if (wac == null) {
wac = createWebApplicationContext(rootContext); // 上面都没有没有就创建
}

if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}

if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}

return wac;
}

这里就是DispatcherServlet尝试从容器中获取已经注册的组件,如果没有就使用默认的

org.springframework.web.servlet.DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}

如果容器中没有指定,spring使用默认的组件,具体配置在DispatcherServlet.properties

org/springframework/web/servlet/DispatcherServlet.properties
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
org.springframework.web.servlet.LocaleResolver=\
org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=\
org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=\
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=\
org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=\
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=\
org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=\
org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=\
org.springframework.web.servlet.support.SessionFlashMapManager
MultipartResolver

MultipartResolver主要用来处理文件上传,spring默认没有multipart处理,因为考虑到一些开发者想自己进行处理。不过它提供了对应的解析器,这样如果想使用spring提供的multipart解析器,只要在spring的应用上下文中添加配置就行。那么每个请求将会被检查是否包含multipart,然后在请求中可以将其当成其它属性一样处理,比如:

1
2
3
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000" />
</bean>
HandlerAdapters
  • HttpRequestHandlerAdapter:Http请求处理适配器,仅仅支持Http请求处理,简单的将Http请求对象和响应对象传递给Http请求处理器的实现,不需要返回值;

  • SimpleControllerHandlerAdapter:简单处理适配器

  • RequestMappingHandlerAdapter:基于注解实现,通过解析声明在控制器上的请求映射信息来确定相应的处理器方法,来处理当前Http请求。处理过程中,通过反射来探测处理方法的参数,然后调用处理方法,并将返回值映射到模型和控制器对象。

请求处理
1. processRequest

以最常用的 get/post 来进行分析,这里统一交给了processRequest处理,首先做了一些前后的处理工作

org.springframework.web.servlet.FrameworkServlet
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
protected final void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
processRequest(request, response);
}

protected final void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
processRequest(request, response);
}

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); // 放到ThreadLocal中记录

try {
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. doService

然后继续在doService中做了一些准备设置工作

org.springframework.web.servlet.DispatcherServlet
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
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(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.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);
}
}
}
}
3. doDispatch

真正的请求分发处理在doDispatch中,其实就是找到对应的处理器及拦截器进行处理

org.springframework.web.servlet.DispatcherServlet
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
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); // 如果是multipart则转换为MultipartHttpServletReauest
multipartRequestParsed = (processedRequest != request);

// 寻找对应的处理器链HandlerExecutionChain,mappedHandler中包含了实际的处理器和所有的拦截器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response); // 404
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

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

// 触发mappedHandler中拦截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

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);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex); // 拦截器afterCompletion
}catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err)); // // 拦截器afterCompletion
}finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) { // 拦截器afterConcurrentHandlingStarted
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
3.1. getHandler

对应前面的initHandlerMappings(context);,这里就是遍历所有加载的HandlerMapping,然后只要有一个匹配返回处理器就行

org.springframework.web.servlet.DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

其实getHandler返回的是一个处理器链,其中包括对应的处理器以及拦截器

org.springframework.web.servlet.handler.AbstractHandlerMapping
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
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
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;
}

getHandlerInternal负责根据url获取对应对应的handler,并组装成HandlerExecutionChain

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // 获取用于匹配的有效url
Object handler = lookupHandler(lookupPath, request); // 根据url获取处理器,其实是获取的处理器链
if (handler == null) {
Object rawHandler = null;
if ("/".equals(lookupPath)) { // "/" 交给RootHandler处理
rawHandler = getRootHandler();
}
if (rawHandler == null) { // 尝试获取DefaultHandler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
if (rawHandler instanceof String) { // 将beanName转换为对应的handler
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); //包装成处理器链
}
}
return handler;
}

最终进行url匹配的任务交给了lookupHandler,并且由它将找到的处理器封装成处理器链HandlerExecutionChain

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping
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
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
Object handler = this.handlerMap.get(urlPath); // 首先根据url精确匹配
if (handler != null) {
if (handler instanceof String) { // 如果是String,从容器中获取对应的handler
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request); // 空实现,留给子类覆盖
return buildPathExposingHandler(handler, urlPath, urlPath, null); // 包装成HandlerExecutionChain调用链
}

// 如果上面没有找到匹配的,再根据通配符找出所有能匹配的
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/");
}
}
}

// 选出匹配度最高的
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}

if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) { // 忽略url的最后一个"/"
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) { // 配了url但是没有配对应的handler
throw new IllegalStateException("Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
// 如果上面匹配度最高的url存在多个,则在下面将其组成执行链
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
return null;
}
3.2. getHandlerAdapter

找到处理器之后,寻找其对应的HandlerAdapter,由于处理器是由开发者配置然后由springmvc进行调用,所以spring无法预知它的实际类型,并且对于不同的处理器类型有不同的调用方式,所以这里加了一层适配器,与其说适配器,其实也可以说成是调用器,其提供一套调用模板,并提供判断是否支持,如果支持就由此适配器负责调用

org.springframework.web.servlet.DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
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");
}

对于默认情况下的请求会交给SimpleControllerHandlerAdapter处理,而其判断是否适配的依据就是处理器是否实现了接口Controller,不管是注解标识还是手动继承实现,其实本质是一样的

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
1
2
3
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
3.3. handle

最后由适配器调用对应的处理器,这里还是以上面SimpleControllerHandlerAdapter为例

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
1
2
3
4
public ModelAndView handle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}

AbstractController中提供了一个调用模板,然后将handleRequestInternal暴露给开发者实现

org.springframework.web.servlet.mvc.AbstractController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}

// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);

// session内同步执行
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}

protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception;
3.4. processDispatchResult

处理完之后就是转到对应的视图进行渲染,不过现在类似jsp这样的方式已经很少用,就不再过多赘述

org.springframework.web.servlet.DispatcherServlet
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
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.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);
}
}
3.5. interceptor

Servelet api定义的servlet过滤器可以在处理每个web请求的前后分别对其进行前后置处理。同样,在springMvc中,可以定义拦截器对由springMvc处理的请求进行前后置处理,并且其可以利用容器的各种特性,比如引用容器中声明的任何bean,只需要在spring的web应用上下文中配置即可。如上面的HandlerExecutionChain,各种拦截器会与处理器一起被组织成一个调用链,然后在处理的过程中分别在不同的阶段进行调用。


参考:

  1. 《spring源码深度解析》 郝佳
  2. https://zhuanlan.zhihu.com/p/107842857