DispatcherServlet工作原理解析

近年来由于Spring Boot的流行以及Struts2自身的缺陷。Spring MVC已经完全将Struts2框架淘汰。成为了Java Web应用主流的MVC框架。虽然一直在使用Spring MVC,也大致知道其工作原理,但是一直没有抽时间来全面的总结一下。今天就下个决心,结合最新版的Spring Boot 2.3.0 对应spring 版本5.3.0的源码来研究一下Spring MVC的核心原理之DispatcherServlet的工作流程。

先总结一下Spring MVC工作的主要流程:
在这里插入图片描述
官方说明:
Spring MVC, as many other web frameworks, is designed around the front controller pattern where a central Servlet, the DispatcherServlet, provides a shared algorithm for request processing, while actual work is performed by configurable delegate components. This model is flexible and supports diverse workflows.

The DispatcherServlet, as any Servlet, needs to be declared and mapped according to the Servlet specification by using Java configuration or in web.xml. In turn, the DispatcherServlet uses Spring configuration to discover the delegate components it needs for request mapping, view resolution, exception handling, and more.

意思是在Spring MVC中使用的前端控制器模式,全局存在一个前端控制器DispatcherServlet用于统一接受请求,然后委托其他组件来处理请求。由此可见,DispatcherServlet是SpringMVC应用中的核心请求处理器。且其本身也是一个Servlet。可以使用Java配置或者web.xml文件来配置。
在这里插入图片描述
这是它的结构图:
在这里插入图片描述

  • DispatcherServlet的创建
    接下来看一下DispatcherServlet在Spring Boot项目中是如何自动装配的。
    在这里插入图片描述
    这是从DispatcherServletAutoConfiguration中摘录的生成DispatcherServlet的入口:
/*
* The bean name for a DispatcherServlet that will be mapped to the root URL "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

/*
* The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
*/
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
	DispatcherServlet dispatcherServlet = new DispatcherServlet();
	dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
	dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
	dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
	dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
	dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
	return dispatcherServlet;
}

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
		WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
	DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
			webMvcProperties.getServlet().getPath());
	registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
	registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
	multipartConfig.ifAvailable(registration::setMultipartConfig);
	return registration;
}
  • DispatcherServlet的初始化
    初始化处理请求时所需的必要组件,当前版本中默认是懒加载,也就是说应用启动后,第一个请求过来时DispatcherServlet才会初始化。执行OnRefresh()方法中的逻辑。如下:
/**
 * This implementation calls {@link #initStrategies}.
 */
@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

这个初始化的过程就是从ApplicationContext中回去对应组件的实例赋值给DispatcherServlet 。以供DispatcherServlet处理请求时使用。下面来介绍一下这些组件的功能。

  1. MultipartResolver
    支持文件上传的组件

  2. LocaleResolver
    支持SpringMVC国际化的组件

  3. ThemeResolver
    主题解析器。类似与LocaleResolver。目前在前后端分离的项目中一般不会用到这个功能。

  4. HandlerMapping
    描述Request 到 Handler或者 Interceptor的映射关系 。HandlerMapping的两个主要实现是RequestMappingHandlerMapping(支持@RequestMapping带注释的方法)和SimpleUrlHandlerMapping(维护对处理程序的URI路径模式的显式注册)。

  5. HandlerAdapter
    顾名思义,Handler的适配器。使用到适配器模式。由于Handler有多种实现形式。如使用注解@RequestMapping标注的handler或者自定义的url映射等等。HandlerAdapter对这些handler进行适配。HandlerAdapter的主要目的是使DispatcherServlet免受这些Handler的实现细节的影响。

  6. HandlerExceptionResolver
    Handler异常处理器。用于处理Request与Handler匹配时或者Handler执行时产生的异常。然后将异常转发给处理错误的Handler或者html页面或者其他目标对象。

  7. RequestToViewNameTranslator
    顾名思义。将请求Request转换为视图名称(字符串形式的视图名)的转换器

  8. ViewResolver
    视图解析器。在handler执行完后会返回ModelAndView对象。ViewResolver就是对这个ModelAndView对象进行解析。最终返回View给前端展示。常用的视图解析有ThymeleafViewResolver,FreeMarkerViewResolver,XmlViewResolver,InternalResourceViewResolver(jsp视图解析器,现在基本不用jsp啦)

  9. FlashMapManager
    FlashMap实例管理器,用于储存和查询FlashMap实例。FlashMap是一个Map的数据结构,用于储存Request中信息,当对Request请求过来时,会储存这个Request中的信息,当对这个请求进行Redirect时,就会将FlashMap中的信息转移到新的请求中,并移除FlashMap中存储的上一个Request的信息。

  • DispatcherServlet处理请求

由于其本身是一个Servlet因此其处理请求的逻辑都在doService方法中,下面继续看源码:

/**
 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
 * for the actual dispatching.
 */
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //打印日志
	logRequest(request);
	// Keep a snapshot of the request attributes in case of an include,
	// to be able to restore the original attributes after the include.
	//保存请求中的参数
	Map<String, Object> attributesSnapshot = null;
	//检查Request中是否包含javax.servlet.include.request_uri属性
	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));
			}
		}
	}

	// 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());

	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()) {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
}

/**
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @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 {
		    //检查请求中是否包含文件。如果是会返回MultipartHttpServletRequest实例,如果不是返回原始request
			processedRequest = checkMultipart(request);
			//因此,如果processedRequest!=request 表示此请求中带有文件
			multipartRequestParsed = (processedRequest != request);

			// Determine handler for the current request.
			//根据请求从HandlerMapping集合中获取请求的执行链。一个包含Handler实例和多个HandlerInterceptor的执行链
			mappedHandler = getHandler(processedRequest);
			if (mappedHandler == null) {
			    //如果没有找到处理此请求的Handler,就抛异常或直接返回页面404.
			    //根据throwExceptionIfNoHandlerFound参数来确定处理逻辑
				noHandlerFound(processedRequest, response);
				return;
			}

			// Determine handler adapter for the current request.
			//根据HandlerExecutionChain去查询对应的HandlerAdapter实例,如果没有找到会直接抛异常
			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

			// Process last-modified header, if supported by the handler.
			//对GET和HEAD请求进行缓存处理
			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;
				}
			}
            //循环执行拦截器的preHandle方法,如果有一个返回为false就终止当前操作
			if (!mappedHandler.applyPreHandle(processedRequest, response)) {
				return;
			}

			// Actually invoke the handler.
			//执行handler中的逻辑,放回一个ModelAndView实例
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

			if (asyncManager.isConcurrentHandlingStarted()) {
				return;
			}
            //设置默认的viewname ,如果mv不为空此段逻辑不执行
			applyDefaultViewName(processedRequest, mv);
			//循环执行拦截器中的postHandle方法
			mappedHandler.applyPostHandle(processedRequest, response, mv);
		}
		catch (Exception ex) {
			dispatchException = ex;
		}
		catch (Throwable err) {
			// As of 4.3, we're processing Errors thrown from handler methods as well,
			// making them available for @ExceptionHandler methods and other scenarios.
			dispatchException = new NestedServletException("Handler dispatch failed", err);
		}
		//处理执行结果,渲染ModelAndView或处理异常
		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()) {
			// Instead of postHandle and afterCompletion
			if (mappedHandler != null) {
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
			}
		}
		else {
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
}

再看一下DispatcherServlet是怎么渲染ModelAndView和怎么处理异常的。

/**
	 * Handle the result of handler selection and handler invocation, which is
	 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
	 */
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
        //如果有异常信息就开始处理错误,遍历HandlerExceptionResolver实例来看谁能处理这个异常 
        //HandlerExceptionResolver最终也是返回一个异常ModelAndView实例
		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);
			}
		}
        //遍历所有的ViewResolver实例来看谁能渲染这个ModelAndView ,执行渲染实体操作
		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
		    //从ModelAndView中获取View实例,调用View.render()来返回可以展示在浏览器的视图
			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()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

根据源码中的逻辑,我整理了一下DispatcherServlet处理请求的流程图,如下:
在这里插入图片描述
说明:
异常处理这边,如果所有的HandlerExceptionResolver都无法处理这个异常,则会直接把这个异常抛出去。此时DispatcherServlet是无法处理这个异常的,但是Servlet containers can render a default error page in HTML 。

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值