SpringBoot
版本:2.3.1.RELEASE
1 DispatcherServletAutoConfiguration
DispatcherServlet
在SpringBoot
中的自动装配是由DispatcherServletAutoConfiguration
完成的。
源码:
// 指定自动配置类的优先级顺序,DispatcherServletAutoConfiguration在自动配置类中拥有最高优先级
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 指定该类为配置类
@Configuration(proxyBeanMethods = false)
// 只有在当前应用是一个Servlet Web应用的时候,这个配置类才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 只有在classpath下存在DispatcherServlet类的时候,这个配置类才生效
@ConditionalOnClass(DispatcherServlet.class)
// 在ServletWebServerFactoryAutoConfiguration配置之后,才会配置这个类
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
/*
* 匹配"/"路径的DispatcherServlet的默认bean名字
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
/*
* ServletRegistrationBean的默认bean名字
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
/**
* =============================================================
* 嵌套配置类DispatcherServletConfiguration
* 1、定义并实例化DispatcherServlet的bean
* 2、实例化MultipartResolver的bean
* =============================================================
*/
// 指定该类为配置类
@Configuration(proxyBeanMethods = false)
// 只有在DefaultDispatcherServletCondition条件类被满足的情况下,这个配置类才生效
@Conditional(DefaultDispatcherServletCondition.class)
// 只有在classpath下存在ServletRegistration类的时候,这个配置类才生效
@ConditionalOnClass(ServletRegistration.class)
// 实例化WebMvcProperties配置属性类
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
// 实例化DispatcherServlet bean,并命名为dispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
// 实例化MultipartResolver bean,名字为multipartResolver
@Bean
// 只有在classpath下存在MultipartResolver类石才生效
@ConditionalOnBean(MultipartResolver.class)
// 只有在DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME实例bean不存在的时候才生效
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// 检查用户时候已经创建了MultipartResolver的实例,但是命名不正确
return resolver;
}
}
/**
* =============================================================
* 嵌套配置类DispatcherServletRegistrationConfiguration
* 1、定义并实例化DispatcherServletRegistrationBean
* 这个bean的目的是将DispatcherServletConfiguration配置类中实例化的dispatcherServlet bean注册到Servlet容器
* =============================================================
*/
// 指定该类为配置类
@Configuration(proxyBeanMethods = false)
// 只有在DispatcherServletRegistrationCondition条件类被满足的情况下,这个配置类才生效
@Conditional(DispatcherServletRegistrationCondition.class)
// 只有在classpath下存在ServletRegistration类的时候,该配置类才生效
@ConditionalOnClass(ServletRegistration.class)
// 实例化WebMvcProperties配置属性类
@EnableConfigurationProperties(WebMvcProperties.class)
// 导入DispatcherServletConfiguration配置类
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
// 实例化DispatcherServletRegistrationBean bean,并命名为dispatcherServletRegistration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
// 只有在名字是dispatcherServlet的DispatcherServlet bean存在时,才生效
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(
message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (dispatchServletBeans.isEmpty()) {
return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
}
return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
.items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
if (!outcome.isMatch()) {
return outcome;
}
return checkServletRegistration(beanFactory);
}
private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {
List<String> servlets = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(
startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
return ConditionOutcome.match();
}
private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays
.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty()) {
if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
}
if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
return ConditionOutcome.noMatch(message.found("servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
private ConditionMessage.Builder startMessage() {
return ConditionMessage.forCondition("DispatcherServlet Registration");
}
}
}
在Springboot Web
应用中,DispatcherServlet
作为一个bean
首先被注册到Spring
容器中。然后SpringBoot
还生成了一个DispatcherServletRegistrationBean
的bean
到Spring
容器中,这个bean
负责将DispatcherServlet
的bean
注册到Servlet
容器中。
2 DispatcherServlet
DispatcherServlet
是Web
应用中的一个Servlet
,称作前端控制器。它是一个派发器,匹配请求并按一定的规则将请求派发到对应的处理器上处理,并最终返回结果。
2.1 DispatcherServlet继承关系
下图是DispatcherServlet
的继承关系图:
简单概括DispatcherServlet
继承关系为:
DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet -> GenericServlet
而GenericServlet
实现了Servlet
接口。
通过类图可以看出,DispatcherServlet
通过继承父类,间接实现了Servlet接口
,因此其本质上依旧是一个Servlet
。
2.2 DispatcherServlet初始化过程
DispatcherServlet类
的设计很巧妙,上层父类不同程度的实现了相关接口的部分方法,并留出了相关方法用于子类覆盖,将不变的部分统一实现,将变化的部分通过预留方法用于子类实现。
Servlet
的初始化,是通过调用init(ServletConfig config)
方法实现的。
通过继承关系可以画出DispatcherServlet
初始化调用链:
上述调用逻辑中比较重要的有三个方法:
FrameworkServlet
抽象类中的initServletBean()
方法FrameworkServlet
抽象类中的initWebApplicationContext()
方法DispatcherServlet
类中的onRefresh()
方法
2.2.1 FrameworkServlet抽象类中的initServletBean()
initServletBean()
源码:
@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 {
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");
}
}
这个方法有final
标识,那么FrameworkServlet
的子类就不可以再重写了。该方法主要是为了调用initWebApplicationContext()
方法,并在前后打印了一些日志信息。
2.2.2 FrameworkServlet抽象类中的initWebApplicationContext()
initWebApplicationContext()
源码:
/**
* 为当前Servlet初始化并发布WebApplicationContext。
* 委托createWebApplicationContext方法去创建context,可以被子类重写。
*/
protected WebApplicationContext initWebApplicationContext() {
// 获取由ContextLoaderListener创建的根IoC容器的上下文
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
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
if (cwac.getParent() == null) {
// 如果当前存在一个子容器,并且其父容器的引用是null,则将根容器作为它的父容器
cwac.setParent(rootContext);
}
// 配置并刷新当前的子容器
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果当前应用不存在子容器,则去查找一下
wac = findWebApplicationContext();
}
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;
}
该方法的主要作用同样是创建一个WebApplicationContext
对象,即Ioc
容器,这里创建的是特定Servlet
拥有的子IoC
容器。
Spring中的父子容器的访问特性:父子容器类似于类的继承关系,子类可以访问父类中的成员变量,而父类不可访问子类的成员变量。同样的,子容器可以访问父容器中定义的Bean,但父容器无法访问子容器定义的Bean。
Spring中创建多个Ioc容器,根IoC容器做为全局共享的IoC容器维护Web应用需要共享的Bean,而子IoC容器根据需求的不同,维护不同的Bean,这样能够做到隔离,保证系统的安全性。
下面,查看createWebApplicationContext
方法的源码:
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
// 判断当前contextClass是否是ConfigurableWebApplicationContext的子类
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 创建子容器上下文对象
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置环境信息
wac.setEnvironment(getEnvironment());
// 设置子容器的父容器
wac.setParent(parent);
// 设置配置文件的位置信息
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 配置并刷新子容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
该方法用于创建一个子IoC容器
并将根IoC容器
做为其父容器,接着进行配置和刷新操作用于构造相关的Bean
。
至此,根IoC容器
以及相关Servlet
的子IoC容器
已经配置完成,子容器
中管理的Bean
一般只被该Servlet
使用,因此,其中管理的Bean
一般是“局部”的,如SpringMVC
中需要的各种重要组件,包括Controller
、Interceptor
、Converter
、ExceptionResolver
等。
相关关系如下图所示:
查看configureAndRefreshWebApplicationContext
方法:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
该方法主要是对子容器进行一些信息的配置,最后会调用refresh()
方法刷新容器。
回到initWebApplicationContext
方法中,当createWebApplicationContext
创建子容器之后,最后调用onRefresh
方法刷新容器:
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);
}
}
onRefresh
的真正实现是在DispatcherServlet
类中,下面是onRefresh
源码:
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
onRefresh()
方法直接调用了initStrategies()
方法。该方法用于初始化创建multipartResovle
来支持图片等文件的上传、本地化解析器、主题解析器、HandlerMapping
处理器映射器、HandlerAdapter
处理器适配器、异常解析器、视图解析器、flashMap
管理器,这些组件都是SpringMVC
开发中的重要组件,相关组件的初始化创建过程均在此完成。
2.3 DispatcherServlet请求流程
Spring MVC
有四大组件:
DispatcherServlet
HandlerMapping
HandlerAdapter
ViewResolver
Spring MVC
处理用户请求的过程就是围绕这四大组件进行的。
如下时序图:
[图]…
请求处理流程文字描述如下:
- 用户请求发送至
DispatcherServlet
类。 DispatcherServlet
遍历配置的所有HandlerMapping
,查找对应的Handler
。
// TODO
2.3.1 方法执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykMs4XUv-1595642843541)(C:\Users\taojie6\AppData\Roaming\Typora\typora-user-images\1594814306943.png)]
上图展示了处理用户请求的方法调用流程:
- 首先请求进入
Servlet
的service(ServletRequest req, ServletResponse res)
方法。 - 实现是
GenericServlet
类的抽象方法service(ServletRequest req, ServletResponse res)
。 - 最终的具体实现是
HttpServlet
类的service(ServletRequest req, ServletResponse res)
方法。首先将req
对象转换为HttpServletRequest
类型,将res
对象转换为HttpServletResponse
类型,然后调用类中的service(HttpServletRequest req, HttpServletResponse resp)
方法。根据请求的方法类型,分别调用doGet
、doPost
、doHead
等方法。 - 具体的实现是在
FrameworkServlet
中,最终统一调用的是processRequest
方法。在processRequest
中,又调用了doService
方法,该方法由DispatcherServlet
实现。
根据层层分析,可以看出对用户请求的处理主要流程是集中在DispatcherServlet
中的doService
方法里面。
2.3.2 处理用户请求的流程
doService
方法源码:
/**
* 暴露请求属性并委托doDispatch方法分发理请求
*/
@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.
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));
}
}
}
// 将框架的一些对象,包括容器的context对象,放入request中,这样就可以很方便的随时拿到它们
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);
}
}
}
}
doService()
方法主要进行一些参数的设置,并将部分参数放入request
请求中,真正执行用户请求并作出响应的方法则为doDispatch()
方法。
doDispatch
方法源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 请求对象
HttpServletRequest processedRequest = request;
// HandlerExecutionChain局部变量
HandlerExecutionChain mappedHandler = null;
// 判断是否解析了文件类型的数据,如果有最终需要清理
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
// ModelAndView局部变量
ModelAndView mv = null;
// 异常对象
Exception dispatchException = null;
try {
// 检查是否包含文件类型的数据
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// (1)从所有的HandlerMapping中搜索获取处理当前请求的HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
// 如果HandlerExecutionChain为null,则没有能够进行处理的Handler,抛出异常
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据查到的HandlerExecutionChain对象中的handler,从所有的HandlerAdapter中搜索获取处理请求的HandlerAdapter
// 如果没找到会抛异常
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;
}
}
// 按顺序依次执行HandlerInterceptor的preHandle方法
// 如果任意一个HandlerInterceptor的preHandle方法没有通过,则不继续进行处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通过HandlerAdapter执行Handler,处理用户请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 逆序执行HandlerInterceptor的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理返回的结果,渲染视图、填充model;如果有异常,渲染异常页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 如果有异常,按倒序执行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
// 如果有异常,按倒序执行所有HandlerInterceptor的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
// 倒序执行所有HandlerInterceptor的afterCompletion方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 如果请求包含文件类型的数据,则进行相关清理工作
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
2.3.2.1 遍历所有HandlerMapping获取HandlerExecutionChain对象
在doDispatch(...)
方法中,(1)
处通过调用getHandler(...)
,传入的是HttpServletRequest
对象,返回一个HandlerExecutionChain
对象,源码如下:
@Nullable
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;
}
该方法遍历了开发者配置的所有HandlerMapping
类,根据request
请求来查找HandlerExecutionChain
,如果找到了,返回第一个找到的HandlerExecutionChain
。
如果没有找到HandlerExecutionChain
,则会调用noHandlerFound(...)
方法:
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
noHandlerFound(...)
源码如下:
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
如果没有找到对应的HandlerExecutionChain
,则会抛出异常NoHandlerFoundException
。
2.3.2.2 获取HandlerAdapter对象
如果找到了HandlerExecutionChain
对象,接下来会调用getHandlerAdapter(...)
方法,传入HandlerExecutionChain
对象中的handler
对象,得到能够支持这个handler
的HandlerAdapter
对象,源码如下:
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");
}
该方法会遍历所有的HandlerAdapter
,调用每个Adapter
的supports(...)
方法,如果支持,就会返回这个HandlerAdapter
。如果遍历完了还没找到,就会抛出ServletException
异常。
2.3.2.3 调用HandlerInteceptor的preHandle方法
找到HandlerAdapter
之后,接下来会先调用HandlerExecutionChain
的applyPreHandle(...)
方法:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
该方法里面,会去调用所有已配置的HandlerInterceptor
的preHandle
方法,源码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
改方法会按照顺序依次调用HandlerInterceptor
的preHandle()
方法,但当任一HandlerInterceptor
的preHandle()
方法返回了false
就不再继续执行其他HandlerInterceptor
的preHandle()
方法,而是直接跳转执行triggerAfterCompletion()
方法:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
这里遍历的下标为interceptorIndex
,该变量在前一个方法applyPreHandle()
方法中赋值,如果preHandle()
方法返回true
该变量加1
。
因此该方法会逆序执行所有preHandle()
方法返回了true
的HandlerInterceptor
的afterCompletion()
方法。