springmvc源码之DispatcherServlet前端控制器

news2024/12/26 23:53:10

系列文章目录

springmvc源码之Web上下文初始化
springmvc源码之DispatcherServlet前端控制器


文章目录

  • 系列文章目录
    • DispatcherServlet前端控制器
      • 配置
        • serlvet3.0扩展
        • 静态资源请求问题
      • 源码分析
        • 初始化
          • 配置刷新WebApplicationContext
          • onRefresh
            • 介绍各个组件
        • 处理请求
          • getHandler
          • ha.handle
            • invokeHandlerMethod
          • processDispatchResult处理请求结果


原文地址 https://zhhll.icu/2020/框架/springmvc/底层剖析/1.DispatcherServlet/

DispatcherServlet前端控制器

配置

DispatcherServlet充当SpringMVC的前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml文件中进行配置

web.xml配置

<!-- springmvc会过滤掉.html的 导致视图解析器无法找到
     如果只是使用jsp资源而未使用html的话可以不配置该项
-->
<servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>*.html</url-pattern>
</servlet-mapping>
<!-- 配置DispatcherServlet -->
<servlet>
  <servlet-name>DispatcherServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- 初始化参数:配置Springmvc配置文件的位置和名称
        默认配置文件为:/WEB-INF/<servlet-name>-servlet.xml
  -->
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
  </init-param>
  <!-- 指定servlet加载顺序,指定的话,tomcat在容器启动的时候就会初始化并加载servlet实例,值越小,越早加载;如果不指定该值,表示调用servlet请求时在初始化并加载servlet实例 -->
  <!-- 在程序启动的时候根据contextConfigLocation配置的xml文件开始配置spring应用上下文,初始化组件 -->
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>DispatcherServlet</servlet-name>
  <!-- url-pattern匹配:
	精确匹配:/a
 目录匹配:/*
扩展名匹配:*.do
默认匹配:/
这里要配置为/,使之所有在web.xml中找不到匹配元素的url,都会交给DispatcherServlet来处理
-->
  <url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 处理静态资源 -->
<mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"></mvc:resources>

serlvet3.0扩展

在servlet3.0中,可以不使用xml配置servlet,容器会在类路径中查找实现ServletContainerInitializer接口的类,会使用它来配置Servlet容器,而在spring中提供了该接口的实现,SpringServletContainerInitializer,该实现类会去查找实现WebApplicationInitializer接口的类来完成配置任务,而AbstractAnnotationConfigDispatcherServletInitializer就是该接口的基础实现,所以可以通过集成AbstractAnnotationConfigDispatcherServletInitializer类来进行配置DispatcherServlet

需要实现三个方法

// 将一个或多个路径映射到DispacherServlet
protected abstract String[] getServletMappings();

// 返回带有@Configuration注解的类用来定义ContextLoaderLister创建应用上下文中的bean
	protected abstract Class<?>[] getRootConfigClasses();

	// 返回带有@Configuration注解的类用来定义DispacherServlet应用上下文中的bean
	protected abstract Class<?>[] getServletConfigClasses();

静态资源请求问题

因为DispatcherServlet的配置的是/,针对的是所有请求,所以对于一些的静态资源(如.js、.css)等也会经过DispatcherServlet,但是DispatcherServlet是处理动态请求的,无法处理静态资源

配置<mvc:default-servlet-handler/>来解决,作用是处理静态资源,将在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,会对进行DispatcherServlet的请求进行筛选,如果发现是没有经过映射的请求,就将请求交给WEB服务器默认的Servlet来处理,否则交由DispatcherServlet来处理

default-servlet-name默认是default,如果不是default需要显式的进行配置(看所使用的web服务器,tomcat是default

<mvc:default-servlet-handler default-servlet-name="default"/>

需要注意的是,配置了<mvc:default-servlet-handler/>之后,@RequestMapping的映射会失效,需要加上<mvc:annotation-driven/>配置

源码分析

先看一下DispatcherServlet结构,其本质就是一个Servlet
在这里插入图片描述

DispatcherServlet的工作大致可以分为两个部分:一是初始化部分,本质就是一个Servlet,在init方法时会进行初始化,最终调用initStrategies方法;二是对于HTTP请求进行响应,调用doGet和doPost方法,最终调用doDispatch方法
在这里插入图片描述

初始化

// DispatcherServlet继承了FrameworkServlet类,FrameworkServlet继承了HttpServletBean类,HttpServletBean继承了HttpServlet,所以DispatcherServlet本质上是一个Servlet
public class DispatcherServlet extends FrameworkServlet

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware
  
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware

HttpServletBean的init方法

public final void init() throws ServletException {

   // Set bean properties from init parameters.
  // 获取初始化参数 init-param
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
        // 将当前的servlet类转换为一个BeanWrapper,使得可以用spring的方式来对init-param参数进行注入
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
        // 注册自定义属性编辑器
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
        // 空实现
         initBeanWrapper(bw);
        // 属性注入
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         
         throw ex;
      }
   }

   // Let subclasses do whatever initialization they like.
  // 调用子类的initServletBean,来进行servletBean的初始化
  // org.springframework.web.servlet.FrameworkServlet#initServletBean
   initServletBean();
}

FrameworkServlet的initServletBean方法

protected final void initServletBean() throws ServletException {
   
   
   long startTime = System.currentTimeMillis();

   try {
     // 初始化上下文
      this.webApplicationContext = initWebApplicationContext();
     // 空实现
      initFrameworkServlet();
   }
   catch (ServletException ex) {
      throw ex;
   }
   catch (RuntimeException ex) {
      throw ex;
   }

}

// 创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化
protected WebApplicationContext initWebApplicationContext() {
  // 这是父容器,获取根上下文,ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,是在ContextLoaderListener初始化过程中建立的
		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) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
            // 根上下文作为该上下文的双亲上下文
						cwac.setParent(rootContext);
					}
          // 刷新上下文环境
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
      // 根据contextAttribute属性从ServletContext中加载WebApplicationContext
			wac = findWebApplicationContext();
		}
  // 通过上述两种方式都没有找到,则说明不存在WebApplicationContext实例,则进行创建
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			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.
      // 调用DispatcherServlet#initStrategies方法,初始化DispatcherServlet的默认策略配置
			onRefresh(wac);
		}
		// 把当前创建的上下文存到ServletContext中
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			
		}

		return wac;
	}

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
		Class<?> contextClass = getContextClass();
		
		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");
		}
  // contextClass使用的是默认的DEFAULT_CONTEXT_CLASS,XmlWebApplicationContext.class
  // 通过反射实例化contextClass
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		wac.setEnvironment(getEnvironment());
  // 配置双亲上下文为传入的根上下文,parent是在ConetextLoderListener中创建的实例
		wac.setParent(parent);
		wac.setConfigLocation(getContextConfigLocation());
// 初始化spring环境
		configureAndRefreshWebApplicationContext(wac);

		return wac;
	}
配置刷新WebApplicationContext
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);
  // 加载配置文件,这里就是执行org.springframework.context.support.AbstractApplicationContext#refresh方法,与spring中加载上下文是一样的
   wac.refresh();
}
onRefresh

用于刷新spring在web功能实现中所必须使用的全局变量

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

// 下边的组件如果没有配置默认会读取org.springframework.web.servlet下的DispatcherServlet.properties
protected void initStrategies(ApplicationContext context) {
  // 初始化MultipartResolver,获取beanName为multipartResolver的bean,没有默认值,必须手动配置,用于处理上传请求,处理方法是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法获取File
   initMultipartResolver(context);
  // 初始化LocaleResolver,获取beanName为localeResolver的bean,如果没有默认DispatcherServlet.properties获取
  //国际化解析器,使用的地方有两个,一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
   initLocaleResolver(context);
  // 初始化ThemeResolver,获取beanName为themeResolver的bean,如果没有默认DispatcherServlet.properties获取
  //用于解析主题。SpringMVC中一个主题对应 一个properties文件,里面存放着跟当前主题相关的所有资源,如图片、css样式等。SpringMVC的主题也支持国际化
   initThemeResolver(context);
  // 初始化HandlerMappings,如果配置detectAllHandlerMappings为false的话,只会获取beanName为handlerMapping的bean,否则会找所有类型为HandlerMapping的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //初始化处理器映射器
   initHandlerMappings(context);
  // 初始化HandlerAdapters,如果配置detectAllHandlerAdapters为false的话,只会获取beanName为handlerAdapter的bean,否则会找所有类型为HandlerAdapter的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //初始化处理器适配器
   initHandlerAdapters(context);
  // 初始化HandlerExceptionResolvers,如果配置detectAllHandlerExceptionResolvers为false的话,只会获取beanName为handlerExceptionResolver的bean,否则会找所有类型为HandlerExceptionResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  // 初始化异常处理器,对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
   initHandlerExceptionResolvers(context);
  // 初始化RequestToViewNameTranslator,获取beanName为viewNameTranslator的bean,如果没有默认DispatcherServlet.properties获取
  //有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要RequestToViewNameTranslator从request获取ViewName了
   initRequestToViewNameTranslator(context);
  // 初始化ViewResolvers,如果配置detectAllViewResolvers为false的话,只会获取beanName为viewResolver的bean,否则会找所有类型为ViewResolver的bean,默认是true,如果没有默认DispatcherServlet.properties获取
  //ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
   initViewResolvers(context);
  // 初始化FlashMapManager,获取beanName为flashMapManager的bean,如果没有默认DispatcherServlet.properties获取
  //用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
   initFlashMapManager(context);
}

上述默认值是从DispatcherServlet.properties中取的

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

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

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

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

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

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

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

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
介绍各个组件

LocaleResolver

国际化处理

  • AcceptHeaderLocaleResolver 基于URL参数的配置,可以读取url中locale=zh_CN来控制国际化参数
  • CookieLocaleResolver 基于cookie的配置,可以通过浏览器的cookie设置获取Locale对象
  • SessionLocaleResolver 基于session的配置,可以公国检验session的预置的属性来解析,如果没有则会根据请求头中的accept-language来确定

ThemeResolver

主题处理,根据主题控制网页风格

  • FixedThemeResolver 选择一个固定的主题
  • CookieThemeResolver 用于实现用户所选的主题,以cookie的形式存放在客户端的机器上
  • SessionThemeResolver 用于主题存放在session中

HandlerAdapter

  • HttpRequestHandlerAdapter 仅支持HTTP请求处理器的适配,将HTTP请求对象和响应对象传递给HTTP请求处理器的实现,不需要返回值,主要应用在基于HTTP的远程调用实现上
  • SimpleControllerHandlerAdapter 将HTTP请求适配到一个控制器的实现进行处理
  • AnnotationMethodHandlerAdapter 在3.2被废弃,使用RequestMappingHandlerAdapter,基于注解的实现

HandlerExceptionResolver

进行异常处理

处理请求

请求过来之后进行统一处理,由service()/doGet()/doPost()等方法调用

// doPost -> processRequest -> doService -> doDispatch
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

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

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;
	// 提取当前线程的LocaleContext和RequestAttributes,在请求结束后进行重置
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 将当前请求的LocaleContext和RequestAttributes绑定到当前线程,LocaleContextHolder和RequestContextHolder中
		initContextHolders(request, localeContext, requestAttributes);

		try {
      // 实际处理请求
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
      // 请求结束后恢复原始状态
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			// 发布消息
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}


protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

		// 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;
  // include请求,对request中的Attribute进行备份
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<String, Object>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

		try {
      // 进行请求分发
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
        // 还原request中的Attribute快照
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

// 真正的请求处理
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 {
        // 检查是不是multipart类型的,如果是会将请求类型转为MultipartHttpServletRequest
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
        // 获取处理当前请求的处理器,根据请求的URL信息去查找匹配的URL的Handler,如果查找成,并返回一个执行链HandlerExecutionChain
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null || mappedHandler.getHandler() == null) {
           // 没有找到对应的handler,返回404
            noHandlerFound(processedRequest, response);
            return;
         }

         // Determine handler adapter for the current request.
        // 根据当前的handler获取对应的HandlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
        // 处理last-modified请求头
         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;
            }
         }
					// 执行拦截器的preHandler方法,按顺序执行,如果执行过程中有拦截器的preHandler方法返回false,则需要执行拦截器的afterCompletion方法,按照反向顺序进行执行 for (int i = this.interceptorIndex; i >= 0; i--)
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // Actually invoke the handler.
        // 调用handler处理器逻辑,即业务代码Controller
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				// 异步处理直接返回
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
				// 如果没有返回view的视图名称,则采用默认的视图名称,添加前缀、后缀
         applyDefaultViewName(processedRequest, mv);
        // 执行拦截器的postHandle方法,按照反向顺序进行执行 for (int i = interceptors.length - 1; i >= 0; i--)
         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);
      }
     // 处理返回结果,包括视图渲染,处理异常,以及拦截器的afterCompletion方法
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
     // 异常处理,以及拦截器的afterCompletion方法
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
     // 异步请求
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
        // 清除multipart上传的资源
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}
doDispatch处理
getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   for (HandlerMapping hm : this.handlerMappings) {
      
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}

这里就以RequestMappingHandlerMapping为例

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  // 获取request对应的handler
   Object handler = getHandlerInternal(request);
   if (handler == null) {
     // 如果没有,则使用默认的handler
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = getApplicationContext().getBean(handlerName);
   }

   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}
ha.handle

通过适配器去调用Handler的实际逻辑,以RequestMappingHandlerAdapter为例

protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   checkRequest(request);

   // Execute invokeHandlerMethod in synchronized block if required.
  // 如果需要session内的同步执行
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
     // 调用用户逻辑
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}
invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
     // 获取数据绑定 initBinder
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
     // 获取@SessionAttributes
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

      AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
      asyncWebRequest.setTimeout(this.asyncRequestTimeout);

      WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
      asyncManager.setTaskExecutor(this.taskExecutor);
      asyncManager.setAsyncWebRequest(asyncWebRequest);
      asyncManager.registerCallableInterceptors(this.callableInterceptors);
      asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

      if (asyncManager.hasConcurrentResult()) {
         Object result = asyncManager.getConcurrentResult();
         mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
         asyncManager.clearConcurrentResult();
         
         invocableMethod = invocableMethod.wrapConcurrentResult(result);
      }
			// 进行方法调用
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      if (asyncManager.isConcurrentHandlingStarted()) {
         return null;
      }

      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}
processDispatchResult处理请求结果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

   boolean errorView = false;

  // 请求处理过程中有异常抛出
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
        // 调用异常处理器处理异常
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // Did the handler return a view to render?
  // 渲染页面
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }
		// 触发Interceptor.afterCompletion
   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

与时代共命运:数智时代的到来意味着什么?

在数字经济时代背景下&#xff0c;各行各业都在全面推动新技术与商业的加速融合&#xff0c;全力驱动商业模式创新。 当下科技迅速发展&#xff0c;而数智化的发展体现的是社会与经济向新范式的根本转变。而这种转变为企业带来的是产业组织模式、现代基础设施体系、科技人才培…

Iceberg-Trino 如何解决链上数据面临的挑战

1.链上数据处理面临的挑战 区块链数据公司&#xff0c;在索引以及处理链上数据时&#xff0c;可能会面临一些挑战&#xff0c;包括&#xff1a; 海量数据。随着区块链上数据量的增加&#xff0c;数据索引将需要扩大规模以处理增加的负载并提供对数据的有效访问。因此&#xff…

应用层概述(计算机网络-应用层)

目录 网络应用 网络应用程序体系结构 客户/服务器体系结构 P2P对等体系结构 网络应用程序体系结构 应用层协议 网络应用 网络应用是计算机网络体系结构的最上层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 我们以一些经典的…

如何选择一个合适的Web存储方案

Web客户端存储是一个现代Web应用必不可少的功能&#xff0c;常见的有Cookie、WebStorage和IndexedDB等&#xff0c;如何选择一个合适的Web存储方案呢&#xff1f; 一. Cookie 1. 为什么要有Cookie? HTTP协议是无状态的&#xff0c;即一次请求和响应就是一次完整地HTTP通信&…

Oracle监听报错相关问题汇总

监听服务报错问题汇总&#xff1a; 1.Windows 2008系统下oracle 11g监听异常死机&#xff0c;无法连接 **C:\Users\administrator>lsnrctl status LSNRCTL for 64-bit Windows: Version 11.2.0.1.0 - Production on 16-9月 -2020 10:09:26 Copyright (c) 1991, 2010, Orac…

(十六).net学习之SOA-WebService

SOA-WebService一、SOA的思想&#xff0c;分布式服务1.SOA2.优势3.数据总线二、建立webservice1.关于webservice2.具体实现三、WCF多宿主协议1、自托管宿主2、windows service宿主3、IIS宿主4、WAS宿主一、SOA的思想&#xff0c;分布式服务 1.SOA SOA:面向服务架构&#xff0…

一个umi4的项目适配到FireFox60.7.1esr版本上的从头到尾

项目场景&#xff1a; 一个使用umi4创建的大屏项目&#xff0c;用户的浏览器使用的是火狐60.7.1的稳定版。然后就报错了&#xff01;&#xff01;&#xff01; 为什么不让用户换谷歌嘞&#xff0c;咱也不知道。那咱就搞兼容吧~~ 贴个浏览器的版本图片&#xff1a; 问题历程 …

【springboot】从解决@valid失效问题 到根据判断放行的更灵活替代方案 再到优雅的打日志

文章目录前言valid失效问题替代方案前言 valid 可以帮助我们节省很多代码 比较方便 但操作失误时 可能会失效 达不到我们预期效果&#xff1b; valid会有个问题 因为注解过于方便 反而会导致拦截后 错误日志的收集会比较麻烦 &#xff0c;以及在面对有时需要拦截 有时不需要拦截…

机器学习中的模型选择和评估

机器学习中的模型选择和评估1. 介绍2. 模型拟合效果2.1欠拟合与过拟合表现方式2.2 避免欠拟合与过拟合的方法3.实例分析3.1鸢尾花数据集3.2 对鸢尾花数据进行聚类1. 介绍 在机器学习系统中&#xff0c;如何训练出更好的模型、如何判断模型的效果&#xff0c;以及模型是否过拟合…

【内存对齐】一篇文章带你看懂内存对齐(万字详细介绍+代码样例)

目录 为什么需要内存对齐 性能 范围 原子性 结论 数据模型 C 的内存对齐 具名要求 平凡类 标准布局类 平凡类与标准布局类总结 标准布局类的内存对齐 普通的标准布局类 带有位域的标准布局类 手动指定对齐大小的标准布局类 非标准布局类的内存对齐 GLSLang 的…

分布式事务(3):AT模式实战-Seata

1 介绍 Seata&#xff08;Simple Extensible Autonomous Transaction Architecture&#xff0c;简单可扩展自治事务框架&#xff09;是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。Seata 开源半年左右&#xff0c;目前已经有接近一万 star&#xff0c;社…

信息系统项目管理师考试总结

1、结果展示 从结果上看&#xff0c;论文刚刚过&#xff0c;案例分析差两份&#xff0c;选择题差五分。 问题一&#xff1a;案例分析中&#xff0c;计算题可以拿到分&#xff0c;提高案例分析的通过率。计算题比较客观。可以在下一次考试中多准备准备。 问题二&#xff1a;客观…

QUIC的诞生

背景 自1999年HTTP/1.1被提出以来&#xff0c;它已经稳定地被使用超过了20个年头。不过经典并不意味着完美&#xff0c;HTTP/1.1中一个连接同一时刻只能处理一个HTTP请求&#xff0c;如果当前的请求没有结束之前&#xff0c;其他的请求只能处于阻塞状 态。这一“对头阻塞”问题…

权限管理的配置思路

1.在main.js中引入permission.js 2.permission.js中通过vuex中getters.access_token进行路由的重定向 直接import ‘.permission’

MSF基本使用和控制台命令

前言 ● Msfconsole使用接口 ○ 最流行的用户接口 ○ 几乎可以使用全部MSF功能 ○ 控制台命令支持TAB自动补全 ○ 支持外部命令的执行&#xff08;系统命令等&#xff09; ○ 点击鼠标启动 / msfconsole -h -q -r -v / exit ○ help / &#xff1f; / help vulns 控制台命令 …

【Python】利用Python实现精准三点定位(经纬度坐标与平面坐标转换法求解)

【Python】利用Python实现精准三点定位&#xff08;经纬度坐标与平面坐标转换法求解&#xff09; 众所周知&#xff0c;如果已知三个点的坐标&#xff0c;到一个未知点的距离&#xff0c;则可以利用以距离为半径画圆的方式来求得未知点坐标。 如果只有两个已知点&#xff0c;则…

【Array数组】面试前基础知识点深度记忆总结

写本篇博客的原因是发现有人遇到了以下误区&#xff0c; 1、在我面试别人的过程中&#xff0c;我想让他说一下数组的一些内置方法和含义&#xff0c;然后他条例思路不太清晰&#xff0c;跳动着说&#xff0c;所以遗漏了很多&#xff0c;或许按照es5到es6是一个指导顺序&#xf…

51单片机——独立按键实验,小白讲解,相互学习

按键介绍&#xff1a; 按键是一种电子开关&#xff0c;使用时轻轻按开关按钮就可式开关接通&#xff0c;当松手时&#xff0c;开关断开。开发板上使用的按键及内部简易图如下图&#xff1a; 按键管脚两端距离长的表示默认是导通状态&#xff0c;距离短的默认是断开状态&#xf…

Pandas-根据数据表1中的字段a,筛选出数据表2中也包含字段a的数据

前言 本文是该专栏的第18篇,后面会持续分享python的数据分析知识,记得关注。 假设现在有个数据分析的需求,如下: 数据表1中有几十万条数据,数据表2中有几万条数据,两张数据表1和2有两个相同的字段phone,现在需要将数据表1和数据表2中,phone字段存在相同的行,保留下来…

redis集群操作

Redis集群1 集群2 集群架构图3 集群细节4 集群搭建4.1.创建集群4.2.查看集群状态4.3.添加主节点4.4.添加从节点4.5.删除副本节点4.6.集群在线分片1 集群 Redis在3.0后开始支持Cluster(模式)模式&#xff0c;目前redis的集群支持节点的自动发现&#xff0c;支持slave-master选举…