[Java/Spring] 深入理解:Spring Web DispatcherServlet

news/2025/1/4 10:31:05/文章来源:https://www.cnblogs.com/johnnyzen/p/18471215

1 概述:Spring Web DispatcherServlet

DispatcherServlet 简介

  • org.springframework.web.servlet.DispatcherServlet 是一个 Servlet,它接收所有的 HTTP 请求,并根据请求的信息将其分发给相应的处理器(Handler)进行处理。
  • 它是 Spring MVC 架构模式中的关键部分,将请求处理逻辑与实际的业务逻辑解耦,使得代码结构更加清晰和易于维护。

https://github.com/spring-projects/spring-framework/blob/v5.2.15.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java

2 DispatcherServlet 的初始化

Step1 Servlet 容器启动

  • 当 Web 应用启动时,Servlet 容器(如 Tomcat)会加载和初始化所有配置的 Servlet
  • 对于 DispatcherServlet,它的初始化过程在这个阶段开始。

Step2 读取配置

Step2.1 Servlet 初始化参数

  • DispatcherServlet 可以从 web.xml(传统方式)或者基于 Java 的配置(如使用@WebServlet注解)中获取初始化参数。

这些参数可以指定配置文件的位置、Spring 应用上下文的名称等信息。
例如,在传统的 web.xml 配置中,可以如下设置:

<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/springmvc-config.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
  • 这里指定了contextConfigLocation参数,告诉 DispatcherServlet 去哪里寻找 Spring MVC 的配置文件。

load-on-startup参数的值大于 0,表示在容器启动时就初始化该 Servlet

Step2.2 加载 Spring 配置文件

  • 根据配置的contextConfigLocation参数,DispatcherServlet 会加载 Spring 的配置文件。

在这个配置文件中,可以定义各种 Spring 组件,如视图解析器、处理器映射器、处理器适配器等。
这些组件对于后续的请求处理至关重要。

Step3 创建 Web 应用上下文

根上下文与子上下文

  • DispatcherServlet 会创建自己的 Web 应用上下文(通常是WebApplicationContext类型)。

这个上下文是整个 Spring MVC 应用的核心。
在一些复杂的应用中,可能还存在根上下文(由ContextLoaderListener创建),而 DispatcherServlet 的上下文是根上下文的子上下文。

组件扫描与初始化

  • 在创建 Web 应用上下文的过程中,会根据配置进行组件扫描
  • 它会查找并实例化所有配置的 Spring 组件,包括控制器(Controller)、服务(Service)等相关类。
  • 同时,会初始化在配置文件中定义的各种 bean,如视图解析器(ViewResolver)会被创建并配置好相应的视图解析策略。

深入分析:Spring Web DispatcherServlet 注册 Serlvet 的过程

Servlet 的注册: 传统 Servlet vs. Servlet 3

  • 传统的Servlet注册是配置在/WEB-INF/web.xml下,在容器启动的时候注册Servlet

  • Servlet 3规范为我们又提供了一种更加方便的注册方法————容器启动的时候扫描jar包,找到jar包下一个为如下文件路径的文件。

META-INF/services/javax.servlet.ServletContainerInitializer

该文件的内容必须是一个实现了javax.servlet.ServletContainerInitializer接口的实现类,该接口下有一个方法会自动被调用。

public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)throws ServletException 

其中第一个参数是一个Set,当该实现类上有注解@HandlesTypes,注解的value属性是一个Class[],这个数组及其子类(或实现类)会被注入到webAppInitializerClasses参数中。

Spring Web DispatcherServlet 注册过程

下面我们看看Spring是如何利用Servlet 3这种方式完成DispatcherServlet的注册。

ServletContainerInitializer

  • spring-web模块下,META-INF/services/javax.servlet.ServletContainerInitializer文件中的内容为:
org.springframework.web.SpringServletContainerInitializer

SpringServletContainerInitializer

  • 该类实现了ServletContainerInitializer接口,并标有@HandlesTypes注解。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package org.springframework.web;import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;@HandlesTypes({WebApplicationInitializer.class}) // 关键注解 : HandlesTypes
public class SpringServletContainerInitializer implements ServletContainerInitializer { // 关键接口 : 实现了 Servlet 3 规范的 ServletContainerInitializer 接口public SpringServletContainerInitializer() {}//关键方法: onStartup public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {List<WebApplicationInitializer> initializers = new LinkedList();Iterator var4;if (webAppInitializerClasses != null) {var4 = webAppInitializerClasses.iterator();while(var4.hasNext()) {Class<?> waiClass = (Class)var4.next();if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) {try {initializers.add((WebApplicationInitializer)ReflectionUtils.accessibleConstructor(waiClass, new Class[0]).newInstance());} catch (Throwable var7) {throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);}}}}if (initializers.isEmpty()) {servletContext.log("No Spring WebApplicationInitializer types detected on classpath");} else {servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");AnnotationAwareOrderComparator.sort(initializers);var4 = initializers.iterator();while(var4.hasNext()) {WebApplicationInitializer initializer = (WebApplicationInitializer)var4.next();initializer.onStartup(servletContext);}}}
}

onStartup方法:

  • 会将WebApplicationInitializer的实现类实例放到List中排序,依次调用onStartup()方法。
  • Spring为我们提供了一个WebApplicationInitializer的抽象实现类AbstractAnnotationConfigDispatcherServletInitializer,通常我们只需要继承此类来完成DispatcherServletspring context的注册。

AbstractDispatcherServletInitializer#onStartup

AbstractAnnotationConfigDispatcherServletInitializer的父类AbstractDispatcherServletInitializer覆盖了onStartup()方法:

//org.springframework.web.servlet.support.AbstractDispatcherServletInitializerpublic void onStartup(ServletContext servletContext) throws ServletException {super.onStartup(servletContext);registerDispatcherServlet(servletContext);
}

首先,调用父类的方法完成root WebApplicationContext的初始化;
然后,调用registerDispatcherServlet()方法完成DispatcherServlet的注册。

AbstractDispatcherServletInitializer#registerDispatcherServlet

//org.springframework.web.servlet.support.AbstractDispatcherServletInitializerprotected void registerDispatcherServlet(javax.servlet.ServletContext servletContext) {String servletName = getServletName();Assert.hasLength(servletName, "getServletName() must not return empty or null");WebApplicationContext servletAppContext = createServletApplicationContext();Assert.notNull(servletAppContext,"createServletApplicationContext() did not return an application " +"context for servlet [" + servletName + "]");FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);Assert.notNull(registration,"Failed to register servlet with name '" + servletName + "'." +"Check if there is another servlet registered under the same name.");registration.setLoadOnStartup(1);registration.addMapping(getServletMappings());registration.setAsyncSupported(isAsyncSupported());Filter[] filters = getServletFilters();if (!ObjectUtils.isEmpty(filters)) {for (Filter filter : filters) {registerServletFilter(servletContext, filter);}}customizeRegistration(registration);
}

暂时只关注Servlet的注册,Spring容器相关的以后介绍,createDispatcherServlet()方法创建了一个DispatcherServlet实例,然后通过servletContext.addServlet()方法完成DispatcherServlet的注册。

protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) {return new DispatcherServlet(servletAppContext);
}

以上就是DispatcherServlet的注册过程。

3 DispatcherServlet 的工作流程

Step1 接收请求

当一个 HTTP 请求到达 Servlet 容器时,容器会根据请求的 URL 将其路由到相应的 Servlet。对于匹配 DispatcherServlet 的请求(通常是通过 Servlet 映射配置),DispatcherServlet 开始处理这个请求。

Step2 请求处理准备

请求的解析与封装

  • DispatcherServlet 首先会对请求进行解析,将 HTTP 请求中的信息(如请求方法、请求参数、请求头、请求路径等)封装到HttpServletRequest对象中。这个对象将在整个请求处理过程中传递和使用。

获取处理器映射器(HandlerMapping)

  • DispatcherServlet 会从其初始化创建的 Web 应用上下文中获取处理器映射器。处理器映射器的作用是根据请求的信息(主要是 URL)找到处理该请求的处理器(Handler)。Spring 提供了多种类型的处理器映射器,如RequestMappingHandlerMapping等,它们通过不同的策略来确定处理器。

Step3 查找处理器

根据请求查找处理器

  • 处理器映射器会根据请求的 URL、请求方法等信息在已经注册的处理器中查找匹配的处理器。例如,如果使用@RequestMapping注解来定义控制器方法的映射,处理器映射器会根据请求的 URL 模式和请求方法(如 GET、POST 等)来确定对应的@RequestMapping注解标注的方法。

返回处理器执行链(HandlerExecutionChain)

  • 一旦找到匹配的处理器,处理器映射器会返回一个包含处理器和相关拦截器(Interceptor)的处理器执行链。拦截器可以在处理器执行前后执行一些额外的逻辑,如权限验证、日志记录等。

Step4 获取处理器适配器(HandlerAdapter)

选择合适的适配器

  • DispatcherServlet 根据处理器的类型从 Web 应用上下文中获取合适的处理器适配器。不同类型的处理器(如基于方法的处理器、基于控制器类的处理器等)需要不同的适配器来执行。

例如,对于使用@RequestMapping注解的处理器方法,会使用RequestMappingHandlerAdapter。

调用处理器适配器执行处理器

  • 处理器适配器负责调用处理器的具体方法,并将请求相关的参数传递给处理器。

在执行过程中,处理器(通常是控制器方法)会执行业务逻辑,可能涉及到调用服务层、数据访问层等,最终返回一个模型和视图(ModelAndView)对象或者其他类型的结果(如直接返回数据等情况)。

Step5 视图解析

获取视图解析器(ViewResolver)

  • 如果处理器返回了一个ModelAndView对象,DispatcherServlet 会从 Web 应用上下文中获取视图解析器。视图解析器根据ModelAndView中指定的视图名称和其他相关信息来确定实际的视图实现。

解析视图

  • 不同的视图解析器有不同的解析策略。例如,InternalResourceViewResolver可以将视图名称解析为 JSP 文件的路径。视图解析器会返回一个View对象,这个对象负责渲染最终的视图。

Step6 视图渲染与响应返回

渲染视图

  • View对象会根据模型数据(如果有)和请求信息对视图进行渲染。如果是 JSP 视图,会将模型数据填充到 JSP 页面中,然后生成最终的 HTML 内容。

返回响应

  • 渲染后的视图内容会被封装到HttpServletResponse对象中,并通过 Servlet 容器返回给客户端,完成整个请求处理流程。

小结

  • 总之,DispatcherServlet 在 Spring MVC 应用中起着至关重要的作用,它的初始化过程构建了整个请求处理的基础架构,而其工作流程则有条不紊地处理每个请求,将用户的请求转化为最终的响应,为开发高效、可维护的 Web 应用提供了有力的支持。

Y DispatcherServlet 完整源码

spring-webmvc : 5.2.15.RELEASE

package/import

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//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.Iterator;
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.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.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;

DispatcherServlet extends FrameworkServlet、属性

public class DispatcherServlet extends FrameworkServlet {public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet";protected static final Log pageNotFoundLogger = LogFactory.getLog("org.springframework.web.servlet.PageNotFound");private static final Properties defaultStrategies;private boolean detectAllHandlerMappings = true;private boolean detectAllHandlerAdapters = true;private boolean detectAllHandlerExceptionResolvers = true;private boolean detectAllViewResolvers = true;private boolean throwExceptionIfNoHandlerFound = false;private boolean cleanupAfterInclude = true;@Nullableprivate MultipartResolver multipartResolver;@Nullableprivate LocaleResolver localeResolver;@Nullableprivate ThemeResolver themeResolver;@Nullableprivate List<HandlerMapping> handlerMappings;@Nullableprivate List<HandlerAdapter> handlerAdapters;@Nullableprivate List<HandlerExceptionResolver> handlerExceptionResolvers;@Nullableprivate RequestToViewNameTranslator viewNameTranslator;@Nullableprivate FlashMapManager flashMapManager;@Nullableprivate List<ViewResolver> viewResolvers;

构造器

    public DispatcherServlet() {this.setDispatchOptionsRequest(true);}public DispatcherServlet(WebApplicationContext webApplicationContext) {super(webApplicationContext);this.setDispatchOptionsRequest(true);}

方法/代码块

setDetectAllHandlerMappings/setDetectAllHandlerAdapters/setDetectAllHandlerExceptionResolvers/setDetectAllViewResolvers/setThrowExceptionIfNoHandlerFound/setCleanupAfterInclude

    public void setDetectAllHandlerMappings(boolean detectAllHandlerMappings) {this.detectAllHandlerMappings = detectAllHandlerMappings;}public void setDetectAllHandlerAdapters(boolean detectAllHandlerAdapters) {this.detectAllHandlerAdapters = detectAllHandlerAdapters;}public void setDetectAllHandlerExceptionResolvers(boolean detectAllHandlerExceptionResolvers) {this.detectAllHandlerExceptionResolvers = detectAllHandlerExceptionResolvers;}public void setDetectAllViewResolvers(boolean detectAllViewResolvers) {this.detectAllViewResolvers = detectAllViewResolvers;}public void setThrowExceptionIfNoHandlerFound(boolean throwExceptionIfNoHandlerFound) {this.throwExceptionIfNoHandlerFound = throwExceptionIfNoHandlerFound;}public void setCleanupAfterInclude(boolean cleanupAfterInclude) {this.cleanupAfterInclude = cleanupAfterInclude;}

onRefresh

    protected void onRefresh(ApplicationContext context) {this.initStrategies(context);}

initStrategies/initMultipartResolver/initLocaleResolver/initThemeResolver

    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);}private void initMultipartResolver(ApplicationContext context) {try {this.multipartResolver = (MultipartResolver)context.getBean("multipartResolver", MultipartResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Detected " + this.multipartResolver);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());}} catch (NoSuchBeanDefinitionException var3) {this.multipartResolver = null;if (this.logger.isTraceEnabled()) {this.logger.trace("No MultipartResolver 'multipartResolver' declared");}}}private void initLocaleResolver(ApplicationContext context) {try {this.localeResolver = (LocaleResolver)context.getBean("localeResolver", LocaleResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Detected " + this.localeResolver);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());}} catch (NoSuchBeanDefinitionException var3) {this.localeResolver = (LocaleResolver)this.getDefaultStrategy(context, LocaleResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No LocaleResolver 'localeResolver': using default [" + this.localeResolver.getClass().getSimpleName() + "]");}}}private void initThemeResolver(ApplicationContext context) {try {this.themeResolver = (ThemeResolver)context.getBean("themeResolver", ThemeResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Detected " + this.themeResolver);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());}} catch (NoSuchBeanDefinitionException var3) {this.themeResolver = (ThemeResolver)this.getDefaultStrategy(context, ThemeResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No ThemeResolver 'themeResolver': using default [" + this.themeResolver.getClass().getSimpleName() + "]");}}}

initHandlerMappings/initHandlerAdapters

    private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerMappings);}} else {try {HandlerMapping hm = (HandlerMapping)context.getBean("handlerMapping", HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);} catch (NoSuchBeanDefinitionException var3) {}}if (this.handlerMappings == null) {this.handlerMappings = this.getDefaultStrategies(context, HandlerMapping.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerMappings declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters = null;if (this.detectAllHandlerAdapters) {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 = (HandlerAdapter)context.getBean("handlerAdapter", HandlerAdapter.class);this.handlerAdapters = Collections.singletonList(ha);} catch (NoSuchBeanDefinitionException var3) {}}if (this.handlerAdapters == null) {this.handlerAdapters = this.getDefaultStrategies(context, HandlerAdapter.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerAdapters declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}

initHandlerExceptionResolvers

    private void initHandlerExceptionResolvers(ApplicationContext context) {this.handlerExceptionResolvers = null;if (this.detectAllHandlerExceptionResolvers) {Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerExceptionResolvers = new ArrayList(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);}} else {try {HandlerExceptionResolver her = (HandlerExceptionResolver)context.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);this.handlerExceptionResolvers = Collections.singletonList(her);} catch (NoSuchBeanDefinitionException var3) {}}if (this.handlerExceptionResolvers == null) {this.handlerExceptionResolvers = this.getDefaultStrategies(context, HandlerExceptionResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No HandlerExceptionResolvers declared in servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}

initRequestToViewNameTranslator

    private void initRequestToViewNameTranslator(ApplicationContext context) {try {this.viewNameTranslator = (RequestToViewNameTranslator)context.getBean("viewNameTranslator", RequestToViewNameTranslator.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Detected " + this.viewNameTranslator.getClass().getSimpleName());} else if (this.logger.isDebugEnabled()) {this.logger.debug("Detected " + this.viewNameTranslator);}} catch (NoSuchBeanDefinitionException var3) {this.viewNameTranslator = (RequestToViewNameTranslator)this.getDefaultStrategy(context, RequestToViewNameTranslator.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No RequestToViewNameTranslator 'viewNameTranslator': using default [" + this.viewNameTranslator.getClass().getSimpleName() + "]");}}}

initViewResolvers

    private void initViewResolvers(ApplicationContext context) {this.viewResolvers = null;if (this.detectAllViewResolvers) {Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);if (!matchingBeans.isEmpty()) {this.viewResolvers = new ArrayList(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.viewResolvers);}} else {try {ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);this.viewResolvers = Collections.singletonList(vr);} catch (NoSuchBeanDefinitionException var3) {}}if (this.viewResolvers == null) {this.viewResolvers = this.getDefaultStrategies(context, ViewResolver.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No ViewResolvers declared for servlet '" + this.getServletName() + "': using default strategies from DispatcherServlet.properties");}}}

initFlashMapManager

    private void initFlashMapManager(ApplicationContext context) {try {this.flashMapManager = (FlashMapManager)context.getBean("flashMapManager", FlashMapManager.class);if (this.logger.isTraceEnabled()) {this.logger.trace("Detected " + this.flashMapManager.getClass().getSimpleName());} else if (this.logger.isDebugEnabled()) {this.logger.debug("Detected " + this.flashMapManager);}} catch (NoSuchBeanDefinitionException var3) {this.flashMapManager = (FlashMapManager)this.getDefaultStrategy(context, FlashMapManager.class);if (this.logger.isTraceEnabled()) {this.logger.trace("No FlashMapManager 'flashMapManager': using default [" + this.flashMapManager.getClass().getSimpleName() + "]");}}}

getThemeSource/getMultipartResolver/getHandlerMappings

    @Nullablepublic final ThemeSource getThemeSource() {return this.getWebApplicationContext() instanceof ThemeSource ? (ThemeSource)this.getWebApplicationContext() : null;}@Nullablepublic final MultipartResolver getMultipartResolver() {return this.multipartResolver;}@Nullablepublic final List<HandlerMapping> getHandlerMappings() {return this.handlerMappings != null ? Collections.unmodifiableList(this.handlerMappings) : null;}

getDefaultStrategy/getDefaultStrategies/createDefaultStrategy

    protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {List<T> strategies = this.getDefaultStrategies(context, strategyInterface);if (strategies.size() != 1) {throw new BeanInitializationException("DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");} else {return strategies.get(0);}}protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {String key = strategyInterface.getName();String value = defaultStrategies.getProperty(key);if (value == null) {return new LinkedList();} else {String[] classNames = StringUtils.commaDelimitedListToStringArray(value);List<T> strategies = new ArrayList(classNames.length);String[] var7 = classNames;int var8 = classNames.length;for(int var9 = 0; var9 < var8; ++var9) {String className = var7[var9];try {Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());Object strategy = this.createDefaultStrategy(context, clazz);strategies.add(strategy);} catch (ClassNotFoundException var13) {throw new BeanInitializationException("Could not find DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var13);} catch (LinkageError var14) {throw new BeanInitializationException("Unresolvable class definition for DispatcherServlet's default strategy class [" + className + "] for interface [" + key + "]", var14);}}return strategies;}}protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {return context.getAutowireCapableBeanFactory().createBean(clazz);}

doService

    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {this.logRequest(request);Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap();Enumeration attrNames = request.getAttributeNames();label95:while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {break label95;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));attributesSnapshot.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());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 {this.doDispatch(request, response);} finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {this.restoreAttributesAfterInclude(request, attributesSnapshot);}}}

logRequest

    private void logRequest(HttpServletRequest request) {LogFormatUtils.traceDebug(this.logger, (traceOn) -> {String params;if (this.isEnableLoggingRequestDetails()) {params = (String)request.getParameterMap().entrySet().stream().map((entry) -> {return (String)entry.getKey() + ":" + Arrays.toString((Object[])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 (this.isEnableLoggingRequestDetails()) {headers = (String)values.stream().map((name) -> {return name + ":" + Collections.list(request.getHeaders(name));}).collect(Collectors.joining(", "));}return message + ", headers={" + headers + "} in DispatcherServlet '" + this.getServletName() + "'";} else {return message;}});}

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 mv = null;Object dispatchException = null;try {processedRequest = this.checkMultipart(request);multipartRequestParsed = processedRequest != request;mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {this.noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = this.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;}this.applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handler dispatch failed", var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (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);}}}

applyDefaultViewName

    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {if (mv != null && !mv.hasView()) {String defaultViewName = this.getDefaultViewName(request);if (defaultViewName != null) {mv.setViewName(defaultViewName);}}}

processDispatchResult

    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) {this.logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException)exception).getModelAndView();} else {Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;mv = this.processHandlerException(request, response, handler, exception);errorView = mv != null;}}if (mv != null && !mv.wasCleared()) {this.render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}} else if (this.logger.isTraceEnabled()) {this.logger.trace("No view rendering, null ModelAndView returned.");}if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}}}

buildLocaleContext

    protected LocaleContext buildLocaleContext(HttpServletRequest request) {LocaleResolver lr = this.localeResolver;return lr instanceof LocaleContextResolver ? ((LocaleContextResolver)lr).resolveLocaleContext(request) : () -> {return lr != null ? lr.resolveLocale(request) : request.getLocale();};}

checkMultipart

    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)) {this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");}} else if (this.hasMultipartException(request)) {this.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 var3) {if (request.getAttribute("javax.servlet.error.exception") == null) {throw var3;}}this.logger.debug("Multipart resolution failed for error dispatch", var3);}}return request;}

hasMultipartException

    private boolean hasMultipartException(HttpServletRequest request) {for(Throwable error = (Throwable)request.getAttribute("javax.servlet.error.exception"); error != null; error = error.getCause()) {if (error instanceof MultipartException) {return true;}}return false;}

cleanupMultipart

    protected void cleanupMultipart(HttpServletRequest request) {if (this.multipartResolver != null) {MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);if (multipartRequest != null) {this.multipartResolver.cleanupMultipart(multipartRequest);}}}

getHandler

    @Nullableprotected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {Iterator var2 = this.handlerMappings.iterator();while(var2.hasNext()) {HandlerMapping mapping = (HandlerMapping)var2.next();HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}

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(404);}}

getHandlerAdapter

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {Iterator var2 = this.handlerAdapters.iterator();while(var2.hasNext()) {HandlerAdapter adapter = (HandlerAdapter)var2.next();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");}

processHandlerException

    @Nullableprotected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {Iterator var6 = this.handlerExceptionResolvers.iterator();while(var6.hasNext()) {HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;} else {if (!exMv.hasView()) {String defaultViewName = this.getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}if (this.logger.isTraceEnabled()) {this.logger.trace("Using resolved error view: " + exMv, ex);} else if (this.logger.isDebugEnabled()) {this.logger.debug("Using resolved error view: " + exMv);}WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName());return exMv;}} else {throw ex;}}

render

    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);String viewName = mv.getViewName();View view;if (viewName != null) {view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);if (view == null) {throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");}} else {view = mv.getView();if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");}}if (this.logger.isTraceEnabled()) {this.logger.trace("Rendering view [" + view + "] ");}try {if (mv.getStatus() != null) {response.setStatus(mv.getStatus().value());}view.render(mv.getModelInternal(), request, response);} catch (Exception var8) {if (this.logger.isDebugEnabled()) {this.logger.debug("Error rendering view [" + view + "]", var8);}throw var8;}}

getDefaultViewName/resolveViewName

    @Nullableprotected String getDefaultViewName(HttpServletRequest request) throws Exception {return this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null;}@Nullableprotected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {if (this.viewResolvers != null) {Iterator var5 = this.viewResolvers.iterator();while(var5.hasNext()) {ViewResolver viewResolver = (ViewResolver)var5.next();View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {return view;}}}return null;}

triggerAfterCompletion

    private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, ex);}throw ex;}

restoreAttributesAfterInclude

    private void restoreAttributesAfterInclude(HttpServletRequest request, Map<?, ?> attributesSnapshot) {Set<String> attrsToCheck = new HashSet();Enumeration attrNames = request.getAttributeNames();while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {attrsToCheck.addAll(attributesSnapshot.keySet());Iterator var8 = attrsToCheck.iterator();while(var8.hasNext()) {String attrName = (String)var8.next();Object attrValue = attributesSnapshot.get(attrName);if (attrValue == null) {request.removeAttribute(attrName);} else if (attrValue != request.getAttribute(attrName)) {request.setAttribute(attrName, attrValue);}}return;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));attrsToCheck.add(attrName);}}

getRequestUri

    private static String getRequestUri(HttpServletRequest request) {String uri = (String)request.getAttribute("javax.servlet.include.request_uri");if (uri == null) {uri = request.getRequestURI();}return uri;}

static 块

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

X 参考文献

  • 01、Spring源码分析 - 01-DispatcherServlet注册过程 - DDKK.COM 弟弟快看
  • Spring DispatcherServlet 详解 - CSDN
  • spring-framework
  • https://github.com/spring-projects/spring-framework/blob/v5.2.15.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/862264.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DVWA靶场File Inclusion (文件包含) 漏洞所有级别通关教程及源码解析

文件包含 文件包含漏洞(File Inclusion Vulnerability)是一种常见的网络安全漏洞,主要出现在应用程序中不安全地处理文件路径时。攻击者可以利用此漏洞执行恶意文件,或者访问不该被访问的文件 1. low 有3个页面随便点击一个,可以在url处发现传参点访问:http://127.0.0.1/…

[Java]代理模式

本篇文章主要是对静态代理和动态代理实现思路的简述,以示例为主,少涉及理论。 如果文中阐述不全或不对的,多多交流。【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18002823 出自【进步*于辰的博客】参考笔记一…

lvs+dr+httpd+keepalived高可用部署

lvs+dr+httpd+keepalived高可用部署参考文档:http://blog.csdn.net/m582445672/article/details/7670015 a、 keepalived 是lvs 的扩展项目,因此它们之间具备良好的兼容性。这点应该是keepalived 部署比其他类似工具能更简洁的原因吧! b、 通过对服务器池对象的健康检查,实…

《数字经济:行业产业链》全景梳理及区域热力图

行业主要上市公司:中兴通讯(000063)、大华股份(002236)、中科创达(300496)、软通动力(301236)、奇安信(688561)、同方股份(600100)、……等 本文核心数据:《数字经济:产业链 》、《数字经济:产业链 - 生态图谱》、《“产业数字化” 和 “数字产业化” 上市公司…

《数字经济行业产业链》全景梳理及区域热力图

行业主要上市公司:中兴通讯(000063)、大华股份(002236)、中科创达(300496)、软通动力(301236)、奇安信(688561)、同方股份(600100)、……等 本文核心数据:《数字经济产业链 》、《数字经济产业链生态图谱》、《产业数字化 和 数字产业化上市公司区域分布 TOP5》、…

【长路经】C#读取文件抛出FileNotFoundException异常

前言 在winform中读取文件信息时,突然抛出了FileNotFoundException的异常,但是本地是有这个文件的。 随后找到了这个文件,查看属性,[位置]属性,多了"\\?\"的前缀,百度得知这是windows对长路经的处理。需要注意: 目前在NetFx框架下,才有这个问题。 在NetCore…

【unity】学习制作类银河恶魔城游戏-5-

在-1---4-中,了解了unity的基础操作,从创建一个人物开始,通过状态机和代码完善人物的各种功能达到预期效果,随着功能的越来越多,状态机的处理也就越发麻烦,因此,从零开始设计一个合理的状态机很有必要 梦开始的地方 创建一个新的项目修改舒服的布局创建状态机,通过脚本…

CH340C 请求 USB 设备描述符失败

简短不看版如果出现上述状况,说明线有问题:线的确是数据线(不是电源线),但是不是能与CH340正常通讯的数据线(数据线的数据传输功能有问题。 需要更换其它数据线.前文该文分享CH340驱动使用过程可能出现的问题,以下出现的问题解决方案都建立在CH340硬件正常的情况下。CH3…

CH32V203F6P6-TSSOP20测试之02

一、问题思考 直接用官方提供的例程,为何下载程序后没有什么响应,难道自己设计的电路有什么不妥? 于是,对于电路进行具体分析,结果发现: 第一、官方的BOOT0采用杜邦线连接,在芯片手册好像找不到关于BOOT0的叙述,仅仅在《CH32V20x评估板说明书》中看到:也就是说,这芯片…

PVC

短线反弹

大语言模型提示技巧(一)-指令要清晰、具体

在与大语言模型对话时,应该提供尽可能清晰和具体的指令来表达希望模型执行的操作,这将有助于模型给出接近你所期待的输出,并降低得到无关或不正确回复的可能性。但需要注意的是,编写清晰的指令不意味着编写简短的指令,事实上,更长的提示实际上更清晰且提供了更多的上下文…

再见2024,一个算法研发的个人年终总结

元旦将近,显然又是一年岁末。 同事开始讨论中午吃什么,以及晚上的跨年计划之类的大问题。 我开始努力回想自己的2024,秉承着毕业以来每年写个人总结的习惯, 也因为近年来自己的节奏和生活越来越快,只能在年终的节点停下来回顾下自己的历程。 前言 我打开了自己的相册,下面…