package org.springframework.web.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.ui.context.ThemeSource;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.util.NestedServletException;
import org.springframework.web.util.WebUtils;
/**
* HTTP请求处理程序/控制器的中央调度程序,例如用于Web UI控制器
* 或基于HTTP的远程服务导出器。派遣到注册处理程序进行处理
* Web请求,提供便利的映射和异常处理功能。
*
* <p>此Servlet非常灵活:几乎可以与任何工作流程一起使用,
* 安装适当的适配器类。它提供以下功能
* 使其区别于其他请求驱动的Web MVC框架:
*
* <ul>
* <li>它基于JavaBeans配置机制。
*
* <li>它可以使用任何{@link HandlerMapping}实现-预先构建或作为一部分提供
* * 应用程序-控制请求到处理程序对象的路由。默认为
* {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}和
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}。
* HandlerMapping对象可以在servlet的应用程序上下文中定义为bean,
* 实现HandlerMapping接口,如果存在,则覆盖默认的HandlerMapping
* 当下。可以给HandlerMappings任何bean名称(它们通过类型进行测试)。
*
* <li>它可以使用任何{@link HandlerAdapter};这允许使用任何处理程序接口。
* 默认适配器为{@link org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter},
* {@link org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter},用于Spring的
* {@link org.springframework.web.HttpRequestHandler}和
* 分别为{@link org.springframework.web.servlet.mvc.Controller}接口。默认值
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter}
* 也将被注册。 HandlerAdapter对象可以作为bean添加到
* 应用程序上下文,覆盖默认的HandlerAdapters。像HandlerMappings一样,
* 可以给HandlerAdapters任何bean名称(它们通过类型进行测试)。
*
* <li>可以通过以下方式指定调度程序的异常解决策略:
* {@link HandlerExceptionResolver},例如将某些异常映射到错误页面。
* 默认为
* {@link org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver},
* {@link org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver},和
* {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver}。
* 可以通过应用程序上下文覆盖这些HandlerExceptionResolvers。
* 可以给HandlerExceptionResolver任何bean名称(它们通过类型进行测试)。
*
* <li>可以通过{@link ViewResolver}指定其视图分辨率策略
* 实现,将符号视图名称解析为View对象。默认为
* {@link org.springframework.web.servlet.view.InternalResourceViewResolver}。
* ViewResolver对象可以作为bean添加到应用程序上下文中,从而覆盖了
* 默认的ViewResolver。可以为ViewResolvers提供任何bean名称(它们通过类型进行测试)。
*
* <li>如果用户未提供{@link View}或视图名称,则已配置
* {@link RequestToViewNameTranslator}将当前请求转换为视图名称。
* 对应的bean名称是“ viewNameTranslator”;默认是
* {@link org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator}。
*
* <li>调度程序解决多部分请求的策略由
* {@link org.springframework.web.multipart.MultipartResolver}实现。
* 包括Apache Commons FileUpload和Servlet 3的实现;典型的
* 选择为{@link org.springframework.web.multipart.commons.CommonsMultipartResolver}。
* MultipartResolver bean名称是“ multipartResolver”;默认为无。
*
* <li>其语言环境解析策略由{@link LocaleResolver}确定。
* 现成的实现通过HTTP accept标头,cookie或会话工作。
* LocaleResolver Bean名称为“ localeResolver”;默认是
* {@link org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver}。
*
* <li>其主题解析策略由{@link ThemeResolver}确定。
* 包含用于固定主题以及cookie和会话存储的实现。
* ThemeResolver bean名称为“ themeResolver”;默认是
* {@link org.springframework.web.servlet.theme.FixedThemeResolver}。
* </ ul>
*
* <p> <b>注意:{@code @RequestMapping}批注仅在以下情况下被处理:
* 对应的{@code HandlerMapping}(用于类型级别的注释)和/或
* 调度程序中有{@code HandlerAdapter}(用于方法级注释)。</ b>
* 默认情况下是这种情况。但是,如果您要定义自定义{@code HandlerMappings}
* 或{@code HandlerAdapters},那么您需要确保相应的自定义
* {@code RequestMappingHandlerMapping}和/或{@code RequestMappingHandlerAdapter}
* 也已定义-如果您打算使用{@code @RequestMapping}。
*
* <p> <b> Web应用程序可以定义任意数量的DispatcherServlet。</ b>
* 每个servlet将在其自己的名称空间中运行,并加载其自己的应用程序上下文
* 带有映射,处理程序等。仅由加载的根应用程序上下文
* {@link org.springframework.web.context.ContextLoaderListener}(如果有)将被共享。
*
* <p>从Spring 3.1开始,{@code DispatcherServlet}现在可以通过网络注入
* 应用程序上下文,而不是在内部创建自己的应用程序。这在Servlet中很有用
* 3.0+环境,支持以编程方式注册servlet实例。
* 有关详细信息,请参见{@link #DispatcherServlet(WebApplicationContext)} Javadoc。
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Chris Beams
* @author Rossen Stoyanchev
* @see org.springframework.web.HttpRequestHandler
* @see org.springframework.web.servlet.mvc.Controller
* @see org.springframework.web.context.ContextLoaderListener
*/
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
/**
* Bean工厂中此名称空间的MultipartResolver对象的知名名称。
*/
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
/**
* 此命名空间的bean工厂中LocaleResolver对象的已知名称。
*/
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
/**
* 此命名空间的bean工厂中ThemeResolver对象的已知名称
*/
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
/**
* 此命名空间的bean工厂中HandlerMapping对象的已知名称。
* *仅在“DetectalHandlerMappings”关闭时使用。
*
* @see #setDetectAllHandlerMappings
*/
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
/**
* 此命名空间的bean工厂中handleRapter对象的已知名称。
* *仅在“DetectalHandlerAdapters”关闭时使用。
*
* @see #setDetectAllHandlerAdapters
*/
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
/**
* 此命名空间的bean工厂中HandlerExceptionResolver对象的已知名称。
* *仅在“DetectalHandlerExceptionResolvers”关闭时使用。
*
* @see #setDetectAllHandlerExceptionResolvers
*/
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
/**
* 此命名空间的bean工厂中的RequestToViewNameTranslator对象的已知名称。
*/
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
/**
* 此命名空间的bean工厂中viewsolver对象的已知名称。
* *仅在“DetectalViewResolvers”关闭时使用。
*
* @see #setDetectAllViewResolvers
*/
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
/**
* 此命名空间的bean工厂中FlashMapManager对象的已知名称。
*/
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
/**
* 保存当前web应用程序上下文的请求属性。
* *否则,只有全局web应用上下文可以通过标记等获得。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#findWebApplicationContext
*/
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
/**
* 请求属性保存当前的LocaleResolver,可由视图检索。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getLocaleResolver
*/
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
/**
* 请求属性保存当前的ThemeResolver,可由视图检索。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getThemeResolver
*/
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
/**
* 请求属性保存当前资源,可由视图检索。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getThemeSource
*/
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
/**
* 保存只读{@code Map<String的请求属性的名称,?>}
* *与“输入”闪存属性保存前一个请求,如果有的话。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getInputFlashMap(HttpServletRequest)
*/
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
/**
* 包含“output”{@link FlashMap}的请求属性的名称
* *为后续请求保存的属性。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getOutputFlashMap(HttpServletRequest)
*/
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
/**
* 保存{@link FlashMapManager}的请求属性的名称。
*
* @see org.springframework.web.servlet.support.RequestContextUtils#getFlashMapManager(HttpServletRequest)
*/
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
/**
* 暴露给异常的请求属性的名称
* *{@link HandlerExceptionResolver}但未呈现视图
* (e.g. setting the status code).
*/
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
/**
* 在找不到请求的映射处理程序时使用的日志类别。
*/
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
/**
* 类路径资源的名称(相对于DispatcherServlet类)
* *它定义DispatcherServlet的默认策略名称。
*/
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
/**
* DispatcherServlet的默认策略属性以开头的公共前缀。
*/
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet";
/**
* 在找不到请求的映射处理程序时使用的其他记录器。
*/
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
private static final Properties defaultStrategies;
static {
//从属性文件加载默认策略实现。
//这是目前严格的内部,并不意味着定制
//应用程序开发人员。
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
/**
* 检测所有handlerMapping,或者只需要“handlerMapping”bean?。
*/
private boolean detectAllHandlerMappings = true;
/**
* 检测所有的handleradopter或只是期望“handleradopter”bean?。
*/
private boolean detectAllHandlerAdapters = true;
/**
* 检测所有handlerExceptionResolver或只需要“handlerExceptionResolver”bean?
*/
private boolean detectAllHandlerExceptionResolvers = true;
/**
* 检测所有的viewsolver或者只需要“viewsolver”bean?。
*/
private boolean detectAllViewResolvers = true;
/**
* 如果找不到处理此请求的处理程序,则引发NoHandlerFoundException?*.
*/
private boolean throwExceptionIfNoHandlerFound = false;
/**
* 在include request?之后执行请求属性的清理?。
*/
private boolean cleanupAfterInclude = true;
/**
* 此servlet使用的多部分解析器。
*/
@Nullable
private MultipartResolver multipartResolver;
/**
* 查找此伺服器的用户。
*/
@Nullable
private LocaleResolver localeResolver;
/**
* 此servlet使用的解释器(ThemeResolver)。
*/
@Nullable
private ThemeResolver themeResolver;
/**
* 此servlet使用的handler映射列表。
*/
@Nullable
private List<HandlerMapping> handlerMappings;
/**
* 此servlet使用的handlerAdapter列表。
*/
@Nullable
private List<HandlerAdapter> handlerAdapters;
/**
* 此servlet使用的HandlerExceptionResolvers列表
*/
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/**
* 此servlet使用的RequestToViewNameTranslator列表
*/
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/**
* 此servlet使用的FlashMapManager
*/
@Nullable
private FlashMapManager flashMapManager;
/**
* 此servlet使用的ViewResolvers
*/
@Nullable
private List<ViewResolver> viewResolvers;
/**
* 创建一个新的{@code DispatcherServlet},它将创建自己的内部web
* *基于默认值和通过servlet提供的值的应用程序上下文
* *初始化参数。通常用于Servlet2.5或更早版本的环境,其中
* *servlet注册的选项是通过{@code web.xml},这需要使用
* *无参数构造函数的。
* *<p>调用{@link\setContextConfigLocation}(init param'contextConfigLocation')
* *将指示哪些XML文件将由
* *{@linkplain\DEFAULT\u CONTEXT类DEFAULT XmlWebApplicationContext}
* *<p>调用{@link#setContextClass}(init param'contextClass')覆盖
* *默认{@code XmlWebApplicationContext},并允许指定替代类,
* *例如{@code AnnotationConfigWebApplicationContext}。
* *<p>调用{@link\setContextInitializerClasses}(init param'contextInitializerClasses')
* *指示应使用哪个{@code ApplicationContextInitializer}类
* *在refresh()之前进一步配置内部应用程序上下文。
*
* @see #DispatcherServlet(WebApplicationContext)
*/
public DispatcherServlet() {
super();
setDispatchOptionsRequest(true);
}
/**
* 使用给定的web应用程序上下文创建一个新的{@code DispatcherServlet}。这个
* *构造函数在基于实例注册的Servlet3.0+环境中非常有用
* *可以通过{@link ServletContext#addServlet}API来实现。
* *<p>使用此构造函数表示以下属性/init参数
* *将被忽略:
* *<ul>
* *<li>{@link#setContextClass(类)}/'contextClass'</li>
* *<li>{@link#setContextConfigLocation(String)}/'contextConfigLocation'</li>
* *<li>{@link#setContextAttribute(String)}/“contextAttribute”</li>
* *<li>{@link#setNamespace(String)}/'命名空间'</li>
* *</ul>
* *<p>给定的web应用程序上下文可能是{@linkplain,也可能不是{@linkplain
* *ConfigurableApplicationContext#refresh()已刷新}。如果它有<strong>没有</strong>
* *已刷新(推荐的方法),则将发生以下情况:
* *<ul>
* *<li>如果给定上下文没有{@linkplain
* *ConfigurableApplicationContext#setParent},根应用程序上下文
* *将被设置为父级</li>
* *<li>如果给定上下文尚未分配{@linkplain
* *ConfigurableApplicationContext#setId},将为其分配一个</li>
* *<li>{@code ServletContext}和{@code ServletConfig}对象将被委托给
* *应用程序上下文</li>
* *<li>{@link#后处理webapplicationcontext}将被调用</li>
* *<li>通过
* *“contextInitializerClasses”init param或通过{@link
* *将应用setContextInitializers}属性</li>
* *如果
* *上下文实现{@link ConfigurableApplicationContext}</li>
* *</ul>
* *如果上下文已经刷新,则在
* *假设用户已按照其特定的
* *需要。
* *<p>有关用法示例,请参见{@link org.springframework.web.WebApplicationInitializer}。
*
* @param webApplicationContext the context to use
* @see #initWebApplicationContext
* @see #configureAndRefreshWebApplicationContext
* @see org.springframework.web.WebApplicationInitializer
*/
public DispatcherServlet(WebApplicationContext webApplicationContext) {
super(webApplicationContext);
setDispatchOptionsRequest(true);
}
/**
* 设置是否检测此servlet上下文中的所有HandlerMapping bean。否则,
* *只需要一个名为“handlerMapping”的bean。
* *<p>默认值为“真”。如果希望此servlet使用单个
* *HandlerMapping,尽管上下文中定义了多个HandlerMapping bean。
*/
public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {
this.detectAllHandlerMappings = detectAllHandlerMappings;
}
/**
* 设置是否检测此servlet上下文中的所有HandlerAdapter bean。否则,
* *只需要一个名为“handlerAdapter”的bean。
* *<p>默认值为“真”。如果希望此servlet使用单个
* *尽管在上下文中定义了多个HandlerAdapter bean,HandlerAdapter仍然有效
*/
public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {
this.detectAllHandlerAdapters = detectAllHandlerAdapters;
}
/**
* 设置是否检测此servlet上下文中的所有HandlerExceptionResolver bean。否则,
* *只需要一个名为“handlerExceptionResolver”的bean。
* *<p>默认值为“真”。如果希望此servlet使用单个
* *HandlerExceptionResolver,尽管上下文中定义了多个HandlerExceptionResolver bean。
*/
public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {
this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;
}
/**
* 设置是否检测此servlet上下文中的所有viewsolver bean。否则,
* *只需要一个名为“viewsolver”的bean。
* *<p>默认值为“真”。如果希望此servlet使用单个
* *尽管在上下文中定义了多个viewsolver bean,viewsolver仍然有效。
*/
public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {
this.detectAllViewResolvers = detectAllViewResolvers;
}
/**
* 设置在找不到此请求的处理程序时是否引发NoHandlerFoundException。
* *然后可以使用HandlerExceptionResolver或
* *{@code@ExceptionHandler}控制器方法。
* *<p>注意如果{@link org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler}
* *使用后,请求将始终转发到默认的servlet和
* *在这种情况下,不会抛出NoHandlerFoundException。
* *<p>默认值为“false”,表示DispatcherServlet通过
* *Servlet响应。
*
* @since 4.0
*/
public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) {
this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound;
}
/**
* 设置是否在include请求之后执行请求属性的清理,即,
* *DispatcherServlet之后是否重置所有请求属性的原始状态
* *已在包含请求中处理。否则,只有调度员自己
* *将重置请求属性,但不重置jsp或特殊属性的模型属性
* *由视图(例如,JSTL)设置。
* *<p>默认值为“true”,强烈建议这样做。视图不应依赖于请求属性
* *已由(动态的)include设置。这允许由包含的控制器呈现JSP视图
* *使用任何模型属性,即使与主JSP中的名称相同,也不会导致
* *效果。只有在特殊需要时才关闭它,例如故意让主jsp
* *从包含的控制器呈现的JSP视图访问属性。
*/
public void setCleanupAfterInclude(boolean cleanupAfterInclude) {
this.cleanupAfterInclude = cleanupAfterInclude;
}
/**
* 这个实现调用{@link#initStrategies}。
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 初始化此servlet使用的策略对象。
* <p>可以在子类中重写,以初始化其他策略对象。
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
/**
* 初始化此类使用的MultipartResolver。
* <p>如果在BeanFactory中没有为此名称空间定义给定名称的bean,
* 不提供多部分处理。
*/
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
/**
* 初始化此类使用的LocaleResolver。
* <p>如果在BeanFactory中没有为此名称空间定义给定名称的bean,
* 我们默认为AcceptHeaderLocaleResolver。
*/
private void initLocaleResolver(ApplicationContext context) {
try {
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
/**
* 初始化该类使用的ThemeResolver。
* *<p>如果在BeanFactory中没有为此命名空间定义具有给定名称的bean,
* *我们默认为FixedThemeResolver。
*/
private void initThemeResolver(ApplicationContext context) {
try {
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.themeResolver);
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}
/**
* 初始化此类使用的HandlerMappings。
* *<p>如果BeanFactory中没有为此命名空间定义HandlerMapping bean,
* *我们默认为BeanNameUrlHandlerMapping。
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
//我们保持handler映射的有序性。
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
//忽略,我们稍后将添加默认HandlerMapping。
}
}
// 通过注册,确保我们至少有一个HandlerMapping
// 如果找不到其他映射,则为默认HandlerMapping。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* 初始化此类中使用的手持适配器。
* *<p>if no handlerandaptant beans are defined in the beanfactory for this namespace,
* *We default to simplecontrollerhandleranformation.
*/
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
//在ApplicationContext中查找所有handlerAdapter,包括祖先上下文。
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// 我们把手电筒按顺序放好。
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
} else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
} catch (NoSuchBeanDefinitionException ex) {
//忽略,我们稍后将添加默认的HandlerAdapter。
}
}
// 确保我们至少有一些手持式适配器,通过注册
// 如果找不到其他适配器,则为默认的handlerAdapter。
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* 初始化这个类使用的HandlerExceptionResolver。
* * <p>如果在BeanFactory中没有为这个名称空间定义给定名称的bean,
* *我们默认没有异常解析器。
*/
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// 在ApplicationContext中查找所有handlerExceptionResolver,包括祖先上下文。
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// 我们将handlerExceptionResolver按顺序排列。
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
} else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
} catch (NoSuchBeanDefinitionException ex) {
// 忽略,没有HandlerExceptionResolver也可以。
}
}
// 通过注册,确保我们至少有一些handlerExceptionResolver
// 如果找不到其他解决程序,则为默认的handlerExceptionResolver。
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* 初始化此servlet实例使用的RequestToViewNameTranslator。
* *<p>如果未配置任何实现,则默认为DefaultRequestToViewNameTranslator。
*/
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
this.viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.viewNameTranslator);
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
if (logger.isTraceEnabled()) {
logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
"': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");
}
}
}
/**
* 初始化这个类使用的ViewResolvers。
* * <p>如果在BeanFactory中没有为此定义ViewResolver bean
* 命名空间,我们默认为InternalResourceViewResolver。
*/
private void initViewResolvers(ApplicationContext context) {
this.viewResolvers = null;
if (this.detectAllViewResolvers) {
// 在ApplicationContext中找到所有的ViewResolvers,包括祖先上下文。
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.viewResolvers = new ArrayList<>(matchingBeans.values());
// 我们保持ViewResolvers有序。
AnnotationAwareOrderComparator.sort(this.viewResolvers);
}
} else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
this.viewResolvers = Collections.singletonList(vr);
} catch (NoSuchBeanDefinitionException ex) {
// 忽略,我们稍后将添加默认的viewsolver。
}
}
// 通过注册,确保我们至少有一个viewsolver
// 如果找不到其他解析程序,则为默认的ViewResolver
if (this.viewResolvers == null) {
this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ViewResolvers declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
/**
* 初始化此servlet实例使用的{@link FlashMapManager}。
* <p>如果没有配置任何实现,则默认为
* {@code org.springframework.web.servlet.support.DefaultFlashMapManager}。
*/
private void initFlashMapManager(ApplicationContext context) {
try {
this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());
} else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.flashMapManager);
}
} catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
this.flashMapManager = getDefaultStrategy(context, FlashMapManager.class);
if (logger.isTraceEnabled()) {
logger.trace("No FlashMapManager '" + FLASH_MAP_MANAGER_BEAN_NAME +
"': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");
}
}
}
/**
* 如果有,返回这个servlet的ThemeSource;否则返回{@code null}。
* *<p>默认情况下,返回WebApplicationContext作为资源,
* *前提是它实现了ThemeSource接口。
*
* @return the ThemeSource, if any
* @see #getWebApplicationContext()
*/
@Nullable
public final ThemeSource getThemeSource() {
return (getWebApplicationContext() instanceof ThemeSource ? (ThemeSource) getWebApplicationContext() : null);
}
/**
* 获取这个servlet的MultipartResolver(如果有的话)。
*
* @return 这个servlet使用的MultipartResolver,如果没有,则使用{@code null}
* *(表示没有多部分支持)
*/
@Nullable
public final MultipartResolver getMultipartResolver() {
return this.multipartResolver;
}
/**
* 返回被检测到的配置{@link HandlerMapping} bean
* 属性的基础上,键入{@link WebApplicationContext}或初始化
* *来自{@literal DispatcherServlet.properties}的默认策略集。
* 注:</strong> This method may return {@code null} if . </strong> This method may return {@code null} if . </strong> This method may return {@code null} if
* *在{@link #onRefresh(ApplicationContext)}之前。
*
* @return an immutable list with the configured mappings, or {@code null}
* if not initialized yet
* @since 5.0
*/
@Nullable
public final List<HandlerMapping> getHandlerMappings() {
return (this.handlerMappings != null ? Collections.unmodifiableList(this.handlerMappings) : null);
}
/**
* 获取这个servlet的MultipartResolver,如果有的话。返回给定策略接口的默认策略对象。
* *<p>默认实现委托给{@link#getDefaultStrategies},
* *列表中应有一个对象。
*
* @param context the current WebApplicationContext
* @param strategyInterface the strategy interface
* @return the corresponding strategy object
* @see #getDefaultStrategies
*/
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
/**
*为给定的策略接口创建默认策略对象列表。
* * <p>默认实现使用“DispatcherServlet”。属性”文件(在相同的
* *包为DispatcherServlet类)来确定类名。它实例化
* *策略通过上下文的BeanFactory对象。
*
* @param context the current WebApplicationContext
* @param strategyInterface the strategy interface
* @return the List of corresponding strategy objects
*/
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
} catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
} catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
} else {
return new LinkedList<>();
}
}
/**
* 创建默认策略。
* * <p>默认实现使用
* * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory # createBean}。
*
* @param context the current WebApplicationContext
* @param clazz the strategy implementation class to instantiate
* @return the fully configured strategy instance
* @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
*/
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
/**
* 公开特定于DispatcherServlet的请求属性,并将其委托给{@link #doDispatch}
* 用于实际调度。
*/
@Override
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));
}
}
}
// 使框架对象可用于处理程序并查看对象。
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()) {
//如果包含,则还原原始属性快照.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
private void logRequest(HttpServletRequest request) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String params;
if (isEnableLoggingRequestDetails()) {
params = request.getParameterMap().entrySet().stream()
.map(entry -> entry.getKey() + ":" + Arrays.toString(entry.getValue()))
.collect(Collectors.joining(", "));
} else {
params = (request.getParameterMap().isEmpty() ? "" : "masked");
}
String queryString = request.getQueryString();
String queryClause = (StringUtils.hasLength(queryString) ? "?" + queryString : "");
String dispatchType = (!request.getDispatcherType().equals(DispatcherType.REQUEST) ?
"\"" + request.getDispatcherType().name() + "\" dispatch for " : "");
String message = (dispatchType + request.getMethod() + " \"" + getRequestUri(request) +
queryClause + "\", parameters={" + params + "}");
if (traceOn) {
List<String> values = Collections.list(request.getHeaderNames());
String headers = values.size() > 0 ? "masked" : "";
if (isEnableLoggingRequestDetails()) {
headers = values.stream().map(name -> name + ":" + Collections.list(request.getHeaders(name)))
.collect(Collectors.joining(", "));
}
return message + ", headers={" + headers + "} in DispatcherServlet '" + getServletName() + "'";
} else {
return message;
}
});
}
/**
* 处理向处理程序的实际分派。
* <p>该处理程序将通过依次应用servlet的HandlerMappings获得。
* HandlerAdapter将通过查询Servlet的已安装HandlerAdapters获得。
* 查找第一个支持处理程序类的对象。
* <p>所有HTTP方法均由该方法处理。由HandlerAdapters或处理程序决定
* 自己决定可接受的方法。
*
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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);
// 确定当前请求的处理程序。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 确定当前请求的处理程序适配器.
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;
}
}
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);
} catch (Exception ex) {
dispatchException = ex;
} catch (Throwable err) {
// 从4.3开始,我们也在处理处理程序方法抛出的错误,
使它们可用于@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()) {
// 而不是在完工后
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// 清理多部分请求使用的所有资源。
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
/**
* 我们需要视图名称转换吗?
*/
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
/**
* 处理处理程序选择和处理程序调用的结果,即
* *要解析为ModelAndView的ModelAndView或异常。
*/
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()) {
// 在转发期间开始的并发处理
return;
}
if (mappedHandler != null) {
// 异常(如果有)已经处理…
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
/**
* 为给定的请求构建LocaleContext,将请求的主语言环境公开为当前语言环境。
* * <p>默认实现使用dispatcher的LocaleResolver获取当前语言环境,
* *在请求期间可能会更改。
*
* @param request current HTTP request
* @return the corresponding LocaleContext
*/
@Override
protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
LocaleResolver lr = this.localeResolver;
if (lr instanceof LocaleContextResolver) {
return ((LocaleContextResolver) lr).resolveLocaleContext(request);
} else {
return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
}
}
/**
* 将请求转换为多部分请求,并使多部分解析器可用。
* <p>如果未设置多部分解析器,则只需使用现有请求。
*
* @param request current HTTP request
* @return the processed request (multipart wrapper if necessary)
* @see MultipartResolver#resolveMultipart
*/
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
}
} else if (hasMultipartException(request)) {
logger.debug("Multipart resolution previously failed for current request - " +
"skipping re-resolution for undisturbed error rendering");
} else {
try {
return this.multipartResolver.resolveMultipart(request);
} catch (MultipartException ex) {
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// 使用下面的常规请求句柄保持处理错误的分派
} else {
throw ex;
}
}
}
}
// 如果之前未返回:返回原始请求。
return request;
}
/**
* 检查“javax.servlet.error。用于多部分异常的“异常”属性。
*/
private boolean hasMultipartException(HttpServletRequest request) {
Throwable error = (Throwable) request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE);
while (error != null) {
if (error instanceof MultipartException) {
return true;
}
error = error.getCause();
}
return false;
}
/**
* 清理给定多部分请求使用的任何资源(如果有的话)。
*
* @param request current HTTP request
* @see MultipartResolver#cleanupMultipart
*/
protected void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null) {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
if (multipartRequest != null) {
this.multipartResolver.cleanupMultipart(multipartRequest);
}
}
}
/**
* 返回此请求的HandlerExecutionChain。
* <p>按顺序尝试所有处理程序映射.
*
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@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;
}
/**
* 找不到处理程序->设置适当的HTTP响应状态。
*
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
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);
}
}
/**
* 返回此处理程序对象的HandlerAdapter。
*
* @param handler 处理程序对象以查找适配器
* @throws ServletException 如果找不到处理程序的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");
}
/**
* 通过已注册的HandlerExceptionResolvers确定一个错误模型和视图。
*
* @param request current HTTP request
* @param response current HTTP response
* @param handler 执行的处理程序,或者{@code null}(如果在异常时没有选择)
* *(例如,如果多部分解析失败)
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to
* @throws Exception if no error ModelAndView found
*/
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// 成功和错误响应可能使用不同的内容类型
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// 检查注册HandlerExceptionResolvers……
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// 对于一个普通的错误模型,我们可能仍然需要查看名称转换…
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
} else if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
/**
* 呈现给定的ModelAndView。
* 这是处理请求的最后一个阶段。它可能涉及通过名称解析视图。
*
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 确定请求的区域设置并将其应用于响应。
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 我们需要解析视图名。
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
} else {
// 不需要查找:ModelAndView对象包含实际的视图对象。
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// 委托给视图对象进行呈现。
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
/**
* 将提供的请求转换为默认视图名。
*
* @param request current HTTP servlet request
* @return the view name (or {@code null} if no default found)
* @throws Exception if view name translation failed
*/
@Nullable
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}
/**
* 将给定的视图名称解析为一个视图对象(要呈现)。
* * <p>默认的实现会询问这个dispatcher的所有ViewResolvers。
* *可以覆盖自定义解析策略,可能基于
* *特定的模型属性或请求参数。
*
* @param viewName the name of the view to resolve
* @param model the model to be passed to the view
* @param locale the current locale
* @param request current HTTP servlet request
* @return the View object, or {@code null} if none found
* @throws Exception if the view cannot be resolved
* (typically in case of problems creating an actual View object)
* @see ViewResolver#resolveViewName
*/
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, ex);
}
throw ex;
}
/**
* 在包含之后恢复请求属性
*
* @param request current HTTP request
* @param attributesSnapshot the snapshot of the request attributes before the include
*/
@SuppressWarnings("unchecked")
private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?, ?> attributesSnapshot) {
// 需要复制到单独的集合这里,以避免副作用
删除属性时的枚举。
Set<String> attrsToCheck = new HashSet<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attrsToCheck.add(attrName);
}
}
//添加可能已删除的属性
attrsToCheck.addAll((Set<String>) attributesSnapshot.keySet());
//遍历要检查的属性,恢复原始值
或在适当时分别删除属性
for (String attrName : attrsToCheck) {
Object attrValue = attributesSnapshot.get(attrName);
if (attrValue == null) {
request.removeAttribute(attrName);
} else if (attrValue != request.getAttribute(attrName)) {
request.setAttribute(attrName, attrValue);
}
}
}
private static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
uri = request.getRequestURI();
}
return uri;
}
}
springmvc源码-DispatcherServlet
最新推荐文章于 2024-08-10 17:21:57 发布