DispatcherServlet源码分析(二)

以下源码分析基于 Spring的5.1.3 版本

先上SpringMVC的流程图:
在这里插入图片描述

我们知道DispatcherServlet是SpringMVC的一个关键类,关键点DispatcherServlet重写了FrameworkServletdoService()方法,doService()又调用了doDispatch()方法,这两个方法是整个程序的关键点。接下来的分析会穿插SpringMVC各个组件来对DispatherServlet进行分析。

初始化组件initStrategies()

DispatcherServlet首先做的就是初始化工作,看initStrategies()方法:

	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);//初始化上传请求组件
		initLocaleResolver(context);//国际化解析器
		initThemeResolver(context);//主题解析器
		initHandlerMappings(context);//处理器映射器
		initHandlerAdapters(context);//处理器适配器
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);//视图解析器
		initFlashMapManager(context);//重定向属性管理器
	}

我们可以看到initStrategies()方法里面初始化了Multipart组件、国际化解析器、主题解析器、一堆处理器映射器、一堆处理器适配器、视图解析器、重定向属性管理器等等,这样初始化后,诸如处理器映射器、处理器适配器等的组件就可以在后面使用了。

doService()

doService()源码如下:

	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		//1、首先判断是不是 include 请求,如果是则对 request 的 Attribute 做个快照备份
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration<?> attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// 提供框架对象处理程序和视图对象。(也就是Spring MVC中特殊的几个bean)
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		// spring mvc支持i18n
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		// spring mvc支持主题
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
		
		// 2、重定向属性处理,对Redirect的支持,解决了POST/Redirect/GET模式问题
		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 {
			// 3、前端处理请求分发
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// 4、等 doDispatcher 处理完之后(如果不是异步调用且未完成)对已备份好的快照进行还原 ,在做完快照后又对 request设置了一些属性。
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

可以看到doService()方法里面首先判断请求是否为include请求,如果是则将request的attributes做一个快照备份。等doDispatcher()处理完后就(如果不是异步调用且未完成)进行还原 ,在做完快照后又对 request 设置了一些属性。

doDispatcher()

doDispatcher()开始真正对请求进行处理分发。这个方法是整个DispatcherServlet的重点,中间会穿插一些SpringMVC的组件来讲,篇幅较长。

上源码(里面的注释会有对源码的解释):

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 {
				//1、先检查请求是不是Multipart请求,如果是则返回已处理的request,否则返回原来的request
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				//2、遍历HandlerMappings集合,根据HandlerMapping来获取HandlerExecutionChain
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);//对response进行处理,返回404
					return;
				}

				//3、根据Handler(该Handler是在由request获取回来的HandlerExecutionChain里面的)返回适配的HandlerAdapter
				// HandlerAdapter里有handle()方法,这个方法就是实际执行Controller方法的地方
				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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				
				//4、HandlerExecutionChain里注册了一系列拦截器(包括你自己定义并注册了的拦截器以及Spring的拦截器),而applyPreHandle()就是执行一系列拦截器的地方
				//如果拦截器不拦截,则继续流程(拦截器会在另一篇博文讲)
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//5、这里调用HandlerAdapter里的handle()方法,这个方法就是实际执行Controller里的方法的地方
				// 处理完后会返回ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//6、用RequestToViewNameTranslator组件生成ViewName字符串并设置到ModelAndView里
				applyDefaultViewName(processedRequest, mv);
				//7、调用HandlerExecutionChain的拦截器的postHandle()方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				//用在下面的processDispatchResult()方法里
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			//8、渲染view视图,以及调用HandlerExecutionChain里的拦截器的afterCompletion()方法
			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);
				}
			}
		}
	}
源码分析

以下前序与上面源码注释的前序对应

1、先利用MultipartResolverisMultipart()来判断请求是不是multipart请求,如果是,则用resolveMultipart()将request包裹成MultipartHttpServletRequest并将其返回,否则返回原request。

	//跳转-->doDispatch()
	processedRequest = checkMultipart(request);
	//------------------------------------------
	protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
			//...
			//  
			return this.multipartResolver.resolveMultipart(request);	
			//...
		}
		return request;
	}

2HandlerMapping有一个getHandler()方法,该方法用来获取HandlerExecutionChain。因为在DispatcherServlet里一开始已经初始化了一堆HandlerMappings,于是遍历所有的已注册HandlerMapping,并调用它的getHandler()方法来获取HandlerExecutionChain

//跳转-->doDispatch()
mappedHandler = getHandler(processedRequest);
//---------------------------------------------
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
}

3、获取HandlerExecutionChain之后,就要获取HandlerAdapter。先从初始化得来的一堆HandlerAdapters中,调用HandlerAdaptersupport()方法看看该HandlerAdapter是否支持该处理器,若支持就返回该HandlerAdapter

	//跳转-->doDispatch()
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	//---------------------------------------------------------------------
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
		//...
	}

4、因为HandlerExecutionChain里面有处理器Handler和拦截器,这里是在调用处理器前,先调用拦截器的preHandle()方法。

	//跳转-->doDispatch()
	if (!mappedHandler.applyPreHandle(processedRequest, response)) {
		return;
	}			
	//--------------------------------------------------------------	
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = 0; i < interceptors.length; i++) {
				HandlerInterceptor interceptor = interceptors[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}

5、执行完拦截器的preHandle()后,程序就调用HandlerAdapterhandle()方法,该方法是真正执行Controller方法的入口,执行完Controller方法后就返回ModelAndView

	//跳转-->doDispatch()
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	//跳转-->HandlerAdapter
	public interface HandlerAdapter {

		boolean supports(Object handler);
	
		ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	
		long getLastModified(HttpServletRequest request, Object handler);
	
}

关于HandlerAdapter接口,SpringMVC有具体的实现类,如:
在这里插入图片描述
我会另开一篇博文详细讲解SpringMVC的各个组件,里面会讲解HandlerAdapter的实现类。

6、这里涉及到RequestToViewNameTranslator这个组件,SpringMVC有一个实现类DefaultRequestToViewNameTranslator。用RequestToViewNameTranslator组件的getViewName(),将HttpServletRequest请求的uri转换成ViewName字符串并设置到ModelAndView里。

	//跳转-->doDispatch()
	applyDefaultViewName(processedRequest, mv);
	//跳转-->applyDefaultViewName()
	private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
		if (mv != null && !mv.hasView()) {
			String defaultViewName = getDefaultViewName(request);
			if (defaultViewName != null) {
				mv.setViewName(defaultViewName);
			}
		}
	}
	//跳转-->getDefaultViewName()
	protected String getDefaultViewName(HttpServletRequest request) throws Exception {
		return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
	}
	//跳转-->DefaultRequestToViewNameTranslator # getViewName()
	public String getViewName(HttpServletRequest request) {
		String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
		return (this.prefix + transformPath(lookupPath) + this.suffix);
	}

7、遍历拦截器,调用拦截器的postHandle()方法

//跳转-->doDispatch()
mappedHandler.applyPostHandle(processedRequest, response, mv);
//--------------------------------------------------------------
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)						  
		throws Exception {
		HandlerInterceptor[] interceptors = getInterceptors();
		if (!ObjectUtils.isEmpty(interceptors)) {
			for (int i = interceptors.length - 1; i >= 0; i--) {
				HandlerInterceptor interceptor = interceptors[i];
				interceptor.postHandle(request, response, this.handler, mv);
			}
		}
	}

8、这个processDispatchResult()处理Handler执行后的结果,将ModelAndView或者Exception(这里又涉及到HandlerExceptionResolver这个组件,将Exception处理成ModelAndView)渲染成视图,然后调用render()生成页面

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;
		//将Exception包裹成ModelAndView
		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);
			}
		}
			//...
			render(mv, request, response);
			//...
		if (mappedHandler != null) {
			//执行拦截器的afterCompletion()方法
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

可以看到render()就是通过resolveViewName()来用ModelAndView里的ModelMap渲染生成View,这里涉及到ViewResolverLocaleResolver组件。

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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
		response.setLocale(locale);

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

ViewNameTranslator以及ViewResolver组件在生成View视图的时候会被调用

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {
		if (this.viewResolvers != null) {
			for (ViewResolver viewResolver : this.viewResolvers) 
				View view = viewResolver.resolveViewName(viewName, locale);
				if (view != null) {
					return view;
				}
			}
		}
		return null;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值