Spring源码学习(8)——SpringMVC

spring框架提供了构建Web应用程序的全功能MVC模块。通过实现servlet接口的DispatcherServlet来封装其核心功能实现,通过将请求分派给处理程序,同时带有可配置的处理程序映射、视图解析、本地语言、主题解析以及上传下载文件支持。

SpringMVC的配置文件

1)配置web.xml

一个Web中可以没有web.xml文件,它主要用来初始化配置信息:例如welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。但SpringMVC的实现原理是通过Servlet拦截所有URL来达到控制的目的,所以web.xml的配置是必须的。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"> <!-- Spring 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> <!-- Spring MVC Dispatcher Servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> </web-app>

这里面有两个关键的配置

  1、contextConfigLocation:Spring容器的所在位置。这个参数是使Web与Spring的配置文件相结合的关键配置

  2、DispatcherServlet:包含了SpringMvc的请求逻辑,Spring使用DispatcherServlet类拦截Web请求并进行相应的逻辑处理。

2)创建Spring配置文件applicationContext.xml

这里的配置就是用来存放应用所需的bean配置信息,和普通的ApplicationContext并无不同。

3)servlet的配置文件

默认的文件名就是servlet名+'_servlet.xml'

在Web启动时,服务器会加载对应于Servlet的配置文件,通常我们将Web部分的配置存放于此配置文件中。

容器之间的关系(个人理解)

这三个配置文件实际上对应的都是容器。首先web.xml对应的容器应该是tomcat加载web应用时首先加载的一个web容器。contextConfigLocation对应的是Spring的webApplicationContext,这是一个IOC容器,主要用来控制反转消除依赖用的。servlet对应的是servlet的容器

首先web容器中会通过listener持有IoC容器,这样web容器可以通过持有的IoC容器获取其中的bean。

其次web容器可以存放多个servlet,并不唯一,tomcat初始化servlet容器时,会将IoC容器作为此servlet容器的父容器,子容器将拥有访问父容器对象的权限,而父容器不可以访问子容器的权限。同时将其存放在web容器中。

1、ContextLoaderListener

我们首先先从web.xml开始。在编程方式的时候我们可以直接将配置信息作为参数传入Spring容器中,但在Web下,我们需要通过context-param的方式注册并使用ContextLoaderListener进行监听读取配置

ContextLoaderListener的作用就是启动Web容器的时候,自动装配applicationContext的配置信息。因为它实现了ServletContextListener这个接口,在启动容器的时候会默认执行它实现的方法,通过这个接口,我们可以在应用处理请求之前向servletContext中(也就是web容器)添加任意对象,这个对象在servletContext启动时被初始化,在整个servletContext运行期间都是可见的。

servletContext在启动之后会调用ServletContextListener的contextInitialized方法

public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
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!");
} else {
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if(logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
} long startTime = System.currentTimeMillis(); try {
if(this.context == null) {
this.context = this.createWebApplicationContext(servletContext);
} if(this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context;
if(!err.isActive()) {
if(err.getParent() == null) {
ApplicationContext elapsedTime = this.loadParentContext(servletContext);
err.setParent(elapsedTime);
} this.configureAndRefreshWebApplicationContext(err, servletContext);
}
} servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader err1 = Thread.currentThread().getContextClassLoader();
if(err1 == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
} else if(err1 != null) {
currentContextPerThread.put(err1, this.context);
} if(logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
} if(logger.isInfoEnabled()) {
long elapsedTime1 = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms");
} return this.context;
} catch (RuntimeException var8) {
logger.error("Context initialization failed", var8);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
throw var8;
} catch (Error var9) {
logger.error("Context initialization failed", var9);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
throw var9;
}
}
}

这个方法做了几件事情

  1、WebApplicationContext存在性验证

如果servletContext中已经包含了此WebApplicationContext的话,就会抛出异常

  2、创建WebApplicationContext实例,通过createWebApplicationContext方法

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class contextClass = this.determineContextClass(sc);
if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
} else {
return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
}
}
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter("contextClass");
if(contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException var4) {
throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
}
} else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
} catch (ClassNotFoundException var5) {
throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
}
}
}

首先先确定WebApplicationContext的类型,在determineContextClass方法中可以看到,如果配置中有contextClass时,则使用此类型,若没有,就是用默认的类型,该类型在defaultStrategies下可以找到

我们可以在ClassLoader.properties文件中看到默认类型是XmlWebApplicationContext

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

确定了context的类型后,就会使用反射实例化容器,之后再调用configureAndRefreshWebApplicationContext方法读取容器配置

  3、将实例记录在servletContext中

存放在servletContext的property中,对应的键为 WebApplicationContext.class.getName() + ".ROOT"

  4、映射当前的类加载器与创建的实例到全局变量currentContextPerThread中

2、DispatcherServlet

servlet在初始化阶段首先会调用其init方法,我们可以再DispatcherServlet的父类HttpServletBean找到方法的实现

public final void init() throws ServletException {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");
} HttpServletBean.ServletConfigPropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if(!pvs.isEmpty()) {
try {
BeanWrapper ex = PropertyAccessorFactory.forBeanPropertyAccess(this);
ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
ex.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(ex);
ex.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if(this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
} throw var4;
}
} this.initServletBean();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully");
} }

  1、首先是对配置中的初始化参数进行封装,也就是servlet中配置的<init-param>,将其封装至propertyValue中。

  2、将当前的servlet实例转换成beanWrapper实例,PropertyAccessorFactory.forBeanPropertyAccess方法是spring中提供的工具方法,主要用于将指定实例转化为Spring中可以处理的BeanWrapper类型的实例

  3、注册Resource的属性编辑器,即在创建Bean的过程中一旦遇到了Resourcel类型的注入就使用ResourceEditor去解析。

  4、将之前封装的propertyValues放到生成的BeanWrapper中。

  5、接下来就是servletBean的初始化initServletBean()

初始化ServletBean的逻辑在FrameworkServlet中

protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'");
if(this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started");
} long startTime = System.currentTimeMillis(); try {
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
} catch (RuntimeException var6) {
this.logger.error("Context initialization failed", var6);
throw var6;
} if(this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization completed in " + elapsedTime + " ms");
} }

  1、WebApplicationContext的初始化

protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if(this.webApplicationContext != null) {
wac = this.webApplicationContext;
if(wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
if(!attrName.isActive()) {
if(attrName.getParent() == null) {
attrName.setParent(rootContext);
} this.configureAndRefreshWebApplicationContext(attrName);
}
}
} if(wac == null) {
wac = this.findWebApplicationContext();
} if(wac == null) {
wac = this.createWebApplicationContext(rootContext);
} if(!this.refreshEventReceived) {
this.onRefresh(wac);
} if(this.publishContext) {
String attrName1 = this.getServletContextAttributeName();
this.getServletContext().setAttribute(attrName1, wac);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
}
} return wac;
}

首先当前的servlet会从servletContext中的缓存中取到WebApplicationContext,就是我们先前通过监听器添加的WebApplicationContext,对应的Key为WebApplicationContext.class.getName() + ".ROOT"

取到的容器是dispacher-servlet的容器的父容器。

接下来就是寻找及创建当前servlet的WebApplicationContext了

  1、通过构造函数的的注入进行初始化

如果当前的servlet已经有了WebApplicationContext的话,说明这是通过构造函数传入的容器,那么接下来就会将之前找到的rootApplicationContext设置为其父容器,并执行configureAndRefreshWebApplicationContext方法

  2、通过contextAttribute进行初始化

如果在web.xml中的servlet设置了contextAttribute这个参数,那么这个容器在servletContext中存放的键名就是这个参数的值。因此接下来就会获取这个容器

  protected WebApplicationContext findWebApplicationContext() {
String attrName = this.getContextAttribute();
if(attrName == null) {
return null;
} else {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext(), attrName);
if(wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
} else {
return wac;
}
}
}

  3、如果上述两步还是没有找到对应的容器,那么就要重新实例化一个WebApplicationContext了

    protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class contextClass = this.getContextClass();
if(this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name \'" + this.getServletName() + "\' will try to create custom WebApplicationContext context of class \'" + contextClass.getName() + "\', using parent context [" + parent + "]");
} if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(this.getContextConfigLocation());
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}

实例化的逻辑和之前的很相似,这里就不再赘述了。从这边我们可以看到最后也执行了configureAndRefreshWebApplicationContext方法。

configureAndRefreshWebApplicationContext

这个方法用来会已经实例化的WebApplicationContext进行配置及刷新

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
if(this.contextId != null) {
wac.setId(this.contextId);
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(this.getServletContext().getContextPath()) + '/' + this.getServletName());
}
} wac.setServletContext(this.getServletContext());
wac.setServletConfig(this.getServletConfig());
wac.setNamespace(this.getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new FrameworkServlet.ContextRefreshListener(null)));
ConfigurableEnvironment env = wac.getEnvironment();
if(env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment)env).initPropertySources(this.getServletContext(), this.getServletConfig());
} this.postProcessWebApplicationContext(wac);
this.applyInitializers(wac);
wac.refresh();
}

方法里主要对设置了一些属性,例如servletContext,servletConfig等,然后就是一些后处理器的执行:postProcessWebApplicationContext是一个空方法,为了支持拓展,applyInitializers方法则是找到在web.xml配置的初始化参数globalInitializerClasses,并执行其中ApplicationContextInitializer的initialize方法。

接下来就是refresh()方法了

这是一个模板方法,在之前将ApplicationContext容器的时候也说到过,不过之前只是对classPathXmlApplicationContext进行了分析,webApplicationContext的实现逻辑其实也类似

接下来到了刷新onRefresh,这也是一个模板方法

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

  1、初始化MultipartResolver

MultipartResolver主要用来处理文件上传的,默认情况下Spring是没有multipart处理的。如果要用的话,需要在容器中添加multipart解析器,这样每个请求都会被检查是否包含multipart,并使用容器中定义的resulver来解析它,常用配置如下:

    <bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxInMemorySize" value="100000"/>
</bean>

解析器就是在initMultipartResolver方法中,被设置到此servlet中。

  2、初始化LocalResolver

支持国际化配置

  3、初始化ThemeResolver

支持主题功能

  4、初始化HandlerMappings

当客户端发起Request时,DispatcherServlet会将Request提交给HandlerMapping,然后根据配置回传给相应的controller。

默认情况下,spring会加载所有实现handlerMapping接口的Bean,如果只期望Spring加载指定的HandlerMapping,那么可以修改Web.xml中DispatcherServlet的初始化参数,将detectAllHandlerMappings设置为false.

这样Spring会查找名为handlerMapping的bean,若没有找到对应bean,spring会在DispatcherServlet.property中查找org.Springframework.web.servlet.HandlerMapping的内容来加载HandlerMapping

  5、初始化HandlerAdapters

支持适配器,将Http请求对象和响应对象传递给Http请求处理器。

  6、初始化HandlerExceptionResolvers

提供异常处理

  7、初始化RequestToViewNameTranslator

  8、初始化ViewResolver

  9、初始化FlashMapManager

提供请求储存属性

DispatcherServlet的逻辑处理

在HttpServlet类中分别提供了相应的服务方法,例如doDelete() doGet()、doOptions()、doPost()、doPut()、doTrace(),根据不同的请求方式,servlet会引导至对应的方法中去。对于DispatcherServlet来说,对于不同的请求方式,都是统一交给processRequest()这一个方法来处理的。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Object failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
this.initContextHolders(request, localeContext, requestAttributes); try {
this.doService(request, response);
} catch (ServletException var17) {
failureCause = var17;
throw var17;
} catch (IOException var18) {
failureCause = var18;
throw var18;
} catch (Throwable var19) {
failureCause = var19;
throw new NestedServletException("Request processing failed", var19);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if(requestAttributes != null) {
requestAttributes.requestCompleted();
} if(this.logger.isDebugEnabled()) {
if(failureCause != null) {
this.logger.debug("Could not complete request", (Throwable)failureCause);
} else if(asyncManager.isConcurrentHandlingStarted()) {
this.logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
} this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
} }

从中我们可以看出具体的逻辑细节转移到了doService方法中了,但在这之前还是做了一些准备和处理工作的、

  1、首先提取了当前线程的LocaleContext以及RequestAttribute属性

  2、根据当前线程创建对应的localeContext和RequestAttribute,并绑定到当前线程

  3、doService做具体逻辑

  4、请求处理结束后恢复线程到原始状态

  5、请求结束后无论成功与否发布事件通知

接下来看doService的逻辑

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if(this.logger.isDebugEnabled()) {
String attributesSnapshot = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult()?" resumed":"";
this.logger.debug("DispatcherServlet with name \'" + this.getServletName() + "\'" + attributesSnapshot + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
} HashMap attributesSnapshot1 = null;
if(WebUtils.isIncludeRequest(request)) {
attributesSnapshot1 = new HashMap();
Enumeration inputFlashMap = request.getAttributeNames(); label108:
while(true) {
String attrName;
do {
if(!inputFlashMap.hasMoreElements()) {
break label108;
} attrName = (String)inputFlashMap.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet")); attributesSnapshot1.put(attrName, request.getAttribute(attrName));
}
} request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
FlashMap inputFlashMap1 = this.flashMapManager.retrieveAndUpdate(request, response);
if(inputFlashMap1 != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap1));
} request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try {
this.doDispatch(request, response);
} finally {
if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot1 != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot1);
} } }

实际上这个方法还是在做一些准备工作,包括生成request的属性快照、设置各种resolver等

完整的请求处理过程在doDispatch中

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
try {
ModelAndView err = null;
Object dispatchException = null; try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if(mappedHandler == null || mappedHandler.getHandler() == null) {
this.noHandlerFound(processedRequest, response);
return;
} HandlerAdapter err1 = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if(isGet || "HEAD".equals(method)) {
long lastModified = err1.getLastModified(request, mappedHandler.getHandler());
if(this.logger.isDebugEnabled()) {
this.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;
} err = err1.handle(processedRequest, response, mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted()) {
return;
} this.applyDefaultViewName(processedRequest, err);
mappedHandler.applyPostHandle(processedRequest, response, err);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
} this.processDispatchResult(processedRequest, response, mappedHandler, err, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
} } finally {
if(asyncManager.isConcurrentHandlingStarted()) {
if(mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if(multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
} }
}

  1、首先会先检查是否是文件上传

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if(this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if(WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
this.logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, this typically results from an additional MultipartFilter in web.xml");
} else if(this.hasMultipartException(request)) {
this.logger.debug("Multipart resolution failed for current request before - skipping re-resolution for undisturbed error rendering");
} else {
try {
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException var3) {
if(request.getAttribute("javax.servlet.error.exception") == null) {
throw var3;
}
} this.logger.debug("Multipart resolution failed for error dispatch", var3);
}
} return request;
}

如果配置了MultipartResolver,并且当前request是multipartContent类型的话,则转换MultipartHttpServletRequest,并且调用MultipartResolver的resolverMultipart方法对文件做解析。

  2、根据request信息寻找对应的Handler

spring默认的handler是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping这两个

如果在配置中配置了<mvc:annotation-driven />的话,会使用RequestMappingHandlerMapping和BeanNameUrlHandlerMapping这两个Handler

我们看一下RequestMappingHandlerMapping的实现逻辑

首先spring会遍历所有的handler的getHandler()方法

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
if(handler == null) {
handler = this.getDefaultHandler();
} if(handler == null) {
return null;
} else {
if(handler instanceof String) {
String executionChain = (String)handler;
handler = this.getApplicationContext().getBean(executionChain);
} HandlerExecutionChain executionChain1 = this.getHandlerExecutionChain(handler, request);
if(CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = this.getCorsConfiguration(handler, request);
CorsConfiguration config = globalConfig != null?globalConfig.combine(handlerConfig):handlerConfig;
executionChain1 = this.getCorsHandlerExecutionChain(request, executionChain1, config);
} return executionChain1;
}
}

根据request找到对应的handler

 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
if(this.logger.isDebugEnabled()) {
this.logger.debug("Looking up handler method for path " + lookupPath);
} this.mappingRegistry.acquireReadLock(); HandlerMethod var4;
try {
HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);
if(this.logger.isDebugEnabled()) {
if(handlerMethod != null) {
this.logger.debug("Returning handler method [" + handlerMethod + "]");
} else {
this.logger.debug("Did not find handler method for [" + lookupPath + "]");
}
} var4 = handlerMethod != null?handlerMethod.createWithResolvedBean():null;
} finally {
this.mappingRegistry.releaseReadLock();
} return var4;
}

这里首先获取了lookupPath对应的就是请求的uri

接下来根据缓存中解析得到的mapping得到对应的HandlerMethod

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = handler instanceof HandlerExecutionChain?(HandlerExecutionChain)handler:new HandlerExecutionChain(handler);
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
Iterator var5 = this.adaptedInterceptors.iterator(); while(var5.hasNext()) {
HandlerInterceptor interceptor = (HandlerInterceptor)var5.next();
if(interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
if(mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
chain.addInterceptor(interceptor);
}
} return chain;
}

然后将配置中的对应拦截器加入到责任链中。

  3、找到对应的HandlerAdapter

找到handlerAdaptor之后会调用其handle方法处理逻辑,对于之前提到的RequestMappingHandlerMapping对应的RequestMappingHandlerAdapter来说,就是执行之前的handlerMethod

上一篇:MYSQL后更改ROOT的密码后无法启动服务报错1067


下一篇:20172319 《Java程序设计教程》第7周学习总结