Spring之SpringMVC前端控制器DispatcherServlet(源码)分析

1.DispatcherServlet作用说明

  DispatcherServlet提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:

1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;

2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);

3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);

4、通过ViewResolver解析逻辑视图名到具体视图实现;

5、本地化解析;

6、渲染具体的视图等;

7、如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。

2.具体实现说明

看着上面的总结肯定知道了大体上的处理过程,但是对于实际的逻辑实现肯定是一头雾水,DispatcherServlet的主要处理是在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 {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
				}
			}
		}
	}

  2.1请求类型处理

  首先看代码:processedRequest = checkMultipart(request);就是为了处理文件类型的请求,如果请求类型是multipart将其转化通过MultipartResolver进行文件上传解析;如果不是就返回原来的请求。

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
			if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
						"this typically results from an additional MultipartFilter in web.xml");
			}
			else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
				logger.debug("Multipart resolution failed for current request before - " +
						"skipping re-resolution for undisturbed error rendering");
			}
			else {
				return this.multipartResolver.resolveMultipart(request);
			}
		}
		// If not returned before: return original request.
		return request;
	}

  首先判断是不是MultipartHttpServletRequest请求,如果是就不需要再处理了,如果是这继续判断是否含有文件解析失败异常,如果没有最后才会调用MultipartResolver接口实现类的resolveMultipart()方法来具体处理这个请求并且最后返回MultipartHttpServletRequest。当然了如果不是一个文件类型的请求就会直接的返回原始请求。

2.2 根据请求获取能够处理该请求的HandlerExecutionChain

doDispatch()方法中的mappedHandler = getHandler(processedRequest);完成了HandlerExecutionChain(处理器运行链,由处理器和处理器拦截器组成)的寻找定位工作。看下如何具体获取HandlerExecutionChain的过程:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

  这个过程就和SpringMVC处理化的过程有关了,SpringMVC初始化中通过initHandlerMappings(ApplicationContext context)方法初始化了List<HandlerMapping> handlerMappings;也就是通过将所有配置的请求映射放到了这个handlerMappings。这个方法的处理过程简单就是循环遍历这个handlerMappings,如果找到了就返回具体的HandlerMapping 中的HandlerExecutionChain,否则就返回null.如果返回的处理器为空,说明没有能够处理这个请求的 HandlerMapping,那么就会调用noHandlerFound(processedRequest, response)进行处理,看下noHandlerFound(processedRequest, response)具体的工作。

	protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (pageNotFoundLogger.isWarnEnabled()) {
			pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
					"] in DispatcherServlet with name ‘" + getServletName() + "‘");
		}
		if (this.throwExceptionIfNoHandlerFound) {
			ServletServerHttpRequest sshr = new ServletServerHttpRequest(request);
			throw new NoHandlerFoundException(
					sshr.getMethod().name(), sshr.getServletRequest().getRequestURI(), sshr.getHeaders());
		}
		else {
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
		}
	}

  设置HTTP响应状态,并且抛出异常。

2.3 获取请求对应的处理器适配器

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());决定了获取请求对应的处理器适配器。看下具体的实现:

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		for (HandlerAdapter ha : this.handlerAdapters) {
			if (logger.isTraceEnabled()) {
				logger.trace("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		throw new ServletException("No adapter for handler [" + handler +
				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
	}

  和获取请求的HandlerExecutionChain对象一样,遍历初始化就完成的handlerAdapters,有返回没有则抛出异常。

2.4 GET 和 HEAD请求处理

对于请求方式是GET或者HEAD的,会判断是否发生改变,如果没有改变就直接返回,否则就继续下去

2.5 调用具体的HandlerAdapter处理请求

方法ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;是使用给定的处理器来处理这个请求,并且返回一个ModelAndView 。以SimpleServletHandlerAdapter为例,其实是调用一个Servlet的Service方法来进行相应的业务逻辑的处理的。

	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((Servlet) handler).service(request, response);
		return null;
	}

  

2.6返回视图的再处理

对上面处理后的视图或者异常转化成一个ModelAndView对象:

	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) {
				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);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name ‘" + getServletName() +
						"‘: assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

  如果有异常则将异常转化成ModelAndView,如果视图不为空,则调用render(mv, request, response);进行视图的解析渲染。

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// Determine locale for request and apply it to the response.
		Locale locale = this.localeResolver.resolveLocale(request);
		response.setLocale(locale);

		View view;
		if (mv.isReference()) {
			// We need to resolve the view name.
			view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
			if (view == null) {
				throw new ServletException("Could not resolve view with name ‘" + mv.getViewName() +
						"‘ in servlet with name ‘" + getServletName() + "‘");
			}
		}
		else {
			// No need to lookup: the ModelAndView object contains the actual View object.
			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() + "‘");
			}
		}

		// Delegate to the View object for rendering.
		if (logger.isDebugEnabled()) {
			logger.debug("Rendering view [" + view + "] in DispatcherServlet with name ‘" + getServletName() + "‘");
		}
		try {
			view.render(mv.getModelInternal(), request, response);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name ‘" +
						getServletName() + "‘", ex);
			}
			throw ex;
		}
	}

  首先获取请求的本地化处理并且将其设置到response中,如果指定了视图名字那么DispatcherServlet就会调用相应的视图解析器来进行解析,返回View对象

	protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
			HttpServletRequest request) throws Exception {

		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				return view;
			}
		}
		return null;
	}

  如果ModelAndView 没有视图引用说明,它已经含有了一个真实的View对象直接过去即可。如果获取的View对象为空则需要爆出异常。最后也是最重要的就是调用View对象的render方法( view.render(mv.getModelInternal(), request, response))进行视图的渲染了。看具体实现是在View的实现抽象类中AbstractView中。

	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isTraceEnabled()) {
			logger.trace("Rendering view with name ‘" + this.beanName + "‘ with model " + model +
				" and static attributes " + this.staticAttributes);
		}

		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
		prepareResponse(request, response);
		renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
	}

  简单的说就是获取数据并且填充,打完收工。

3.总结

  上面便是按照DispatcherServlet从接受到一个请求到请求完全处理完成所经历的一个过程,很是清晰明确。设计也很简单。从设计到实现,可以察觉设计模式的几大原则和具体的设计都有涉及,从中也学到了很多东西,接着学习,加油。

时间: 2024-10-16 14:14:20

Spring之SpringMVC前端控制器DispatcherServlet(源码)分析的相关文章

涨姿势:Spring Boot 2.x 启动全过程源码分析

上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我们继续往下面分析其核心 run 方法. [toc] SpringApplication 实例 run 方法运行过程 上面分析了 SpringApplication 实例对象构造方法初始化过程,下面继续来看下这个 SpringApplication 对象的 run 方法的源码和运行流程. public Conf

Spring 循环引用(二)源码分析

Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) Spring 循环引用相关文章: <Spring 循环引用(一)一个循环依赖引发的 BUG>:https://www.cnblogs.com/binarylei/p/10325698.html <Spring 循环引用(二)源码分析>:https://www.cnblogs.com/binarylei/p/1032604

SpringMVC加载WebApplicationContext源码分析

from http://blessht.iteye.com/blog/2121845 Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC源码分析介绍它的核心实现原理. Tomcat服务器启动入口文件是web.xml,通过在其中配置相关的Listener和Servlet即可加载Spring MVC所需数据.基于Spring MVC最简单的配置如下.

SpringMVC处理静态文件源码分析

SpringMVC处理静态资源,主要是两个标签,mvc:resources和mvc:default-servlet-handler.在详细说明他们的原理之前,需要先简单说明下SpringMVC中请求处理机制:HandlerMapping和HandlerAdapter. 1 HandlerMapping和HandlerAdapter的来由 用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了 然

【Spring】DispatcherServlet源码分析

使用过HttpServlet的都应该用过其doGet和doPost方法,接下来看看DispatcherServlet对这两个方法的实现(源码在DispatcherServlet的父类FrameworkServlet中): @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { proce

springMVC容器加载源码分析

springmvc是一个基于servlet容器的轻量灵活的mvc框架,在它整个请求过程中,为了能够灵活定制各种需求,所以提供了一系列的组件完成整个请求的映射,响应等等处理.这里我们来分析下springMVC的源码. 首先,spring提供了一个处理所有请求的servlet,这个servlet实现了servlet的接口,就是DispatcherServlet.把它配置在web.xml中,并且处理我们在整个mvc中需要处理的请求,一般如下配置: <servlet> <servlet-name

SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. Hand

[转]SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. Hand

【Spring Boot源码分析】@EnableAutoConfiguration注解(一)@AutoConfigurationImportSelector注解的处理

Java及Spring Boot新手,首次尝试源码分析,欢迎指正! 一.概述 @EnableAutoConfiguration注解是Spring Boot中配置自动装载的总开关.本文将从@EnableAutoConfiguration入手,尝试通过源码分析增强对Spring Boot的理解. 所用版本:Spring Boot 2.2.0.M5 + Spring Framework 5.2.0.RC1 1. 功能的实现:(Spring Boot部分) boot.autoconfigure.Enab