Spring MVC 源码学习札记(一)DispatcherServlet阅读

吾日五省我身 2012-01-19

马上过年了~闲来无事,阅读Spring源码提升自己,做事要有个流程,大家都知道Spring MVC的请求处理流程

Spring MVC 源码学习札记(一)DispatcherServlet阅读

请求进来后必须经由Front controller,而在spring mvc中Front controller就是DispatcherServlet

在我看来,DispatcherServlet作为分发请求交给Controller处理的spring mvc门卫,就是阅读spring mvc源代码的入口。

什么都是由浅入深,看不懂的我搁着  先看能看懂的,对spring mvc用透彻了再回头看看不懂的,我相信能更理解它

/**
	 * Initialize the ViewResolvers used by this class.
	 * <p>If no ViewResolver beans are defined in the BeanFactory for this
	 * namespace, we default to InternalResourceViewResolver.
	 */
	private void initViewResolvers(ApplicationContext context) {
		this.viewResolvers = null;

		if (this.detectAllViewResolvers) {
			// Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
			Map<String, ViewResolver> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
				// We keep ViewResolvers in sorted order.
				OrderComparator.sort(this.viewResolvers);
			}
		}
		else {
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

		// Ensure we have at least one ViewResolver, by registering
		// a default ViewResolver if no other resolvers are found.
		if (this.viewResolvers == null) {
			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
			}
		}
	}

  上面这段代码从字面和注释意思可以看出是初始化ViewResolver,从ApplicationContext中获取

OrderComparator.sort(this.viewResolvers);

 这一段根据经验可以想象出当获取了所有的viewResolver后,对其进行排序,根据当然就是它的order属性,在***-servlet.xml中,配置多个viewResolver时,我们经常会设置order,以保证查找的优先顺序。

/** Detect all ViewResolvers or just expect "viewResolver" bean? */
	private boolean detectAllViewResolvers = true;

  在DispatcherServlet中是否查找ApplicationContext的所有viewResolver,这个属性默认为true

else {
			try {
				ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
				this.viewResolvers = Collections.singletonList(vr);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default ViewResolver later.
			}
		}

  如果非true则表示不会查找所有viewResolver,那么根据VIEW_RESOLVER_BEAN_NAME = "viewResolver"去查找对应的单个viewResolver。

if (this.viewResolvers == null) {
			this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
			if (logger.isDebugEnabled()) {
				logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
			}
		}

  如果上面的true和false的情况都未找到对应viewResolver,就使用默认的ViewResolver.class 即 InternalResourceViewResolver。

  下面来说请求分发的最重要的方法,这里会有很多难点,但是不怕,最重要是走通整个过程:

/**
	 * 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;
		int interceptorIndex = -1;

		try {
			ModelAndView mv;
			boolean errorView = false;

			try {
				processedRequest = checkMultipart(request);

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

				// Apply preHandle methods of registered interceptors.
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
				if (interceptors != null) {
					for (int i = 0; i < interceptors.length; i++) {
						HandlerInterceptor interceptor = interceptors[i];
						if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
							triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
							return;
						}
						interceptorIndex = i;
					}
				}

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

				// Do we need view name translation?
				if (mv != null && !mv.hasView()) {
					mv.setViewName(getDefaultViewName(request));
				}

				// Apply postHandle methods of registered interceptors.
				if (interceptors != null) {
					for (int i = interceptors.length - 1; i >= 0; i--) {
						HandlerInterceptor interceptor = interceptors[i];
						interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
					}
				}
			}
			catch (ModelAndViewDefiningException ex) {
				logger.debug("ModelAndViewDefiningException encountered", ex);
				mv = ex.getModelAndView();
			}
			catch (Exception ex) {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(processedRequest, response, handler, ex);
				errorView = (mv != null);
			}

			// Did the handler return a view to render?
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, 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");
				}
			}

			// Trigger after-completion for successful outcome.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
		}

		catch (Exception ex) {
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}
		catch (Error err) {
			ServletException ex = new NestedServletException("Handler processing failed", err);
			// Trigger after-completion for thrown exception.
			triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
			throw ex;
		}

		finally {
			// Clean up any resources used by a multipart request.
			if (processedRequest != request) {
				cleanupMultipart(processedRequest);
			}
		}
	}

   通读上面的代码首先遇到第一个问题:

processedRequest = checkMultipart(request);

   这就需要来看看checkMultipart方法了。

/**
	 * Convert the request into a multipart request, and make multipart resolver available.
	 * <p>If no multipart resolver is set, simply use the existing request.
	 * @param request current HTTP request
	 * @return the processed request (multipart wrapper if necessary)
	 * @see MultipartResolver#resolveMultipart
	 */
	protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
			if (request instanceof MultipartHttpServletRequest) {
				logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
						"this typically results from an additional MultipartFilter in web.xml");
			}
			else {
				return this.multipartResolver.resolveMultipart(request);
			}
		}
		// If not returned before: return original request.
		return request;
	}

首先进行request是否是MultipartHttpServletRequest的判断

if (this.multipartResolver != null && this.multipartResolver.isMultipart(request))
//multipartResolver与viewResolver一样都有init方法进行初始化。
Request is already a MultipartHttpServletRequest - if not in a forward, " +
						"this typically results from an additional MultipartFilter in web.xml

 告诉我们request在web.xml中配置的MultipartFilter拦截过,已经被转换为MultipartHttpServletRequest,如果没有转换,则在else语句中进行转换。

 当判断request并非MultipartHttpServletRequest实例时,直接返回。

 代码继续往下读,我们可以看到第二个问题了:

// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest, false);

  了解过HandlerMapping接口的同学都知道

public interface HandlerMapping {

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

 就是用来查找处理request所对应的HandlerExecutionChain,对HandlerExecutionChain的理解,以我的知识,只能给出大概的意思,就是Adapter和一组interceptor,Adapter用来干嘛呢,造Controller吧,暂时这么认为。下面就来看Dispatcher里的handler方法。

/**
	 * Return the HandlerExecutionChain for this request. Try all handler mappings in order.
	 * @param request current HTTP request
	 * @param cache whether to cache the HandlerExecutionChain in a request attribute
	 * @return the HandlerExceutionChain, or <code>null</code> if no handler could be found
	 */
	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
		HandlerExecutionChain handler = (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
		if (handler != null) {
			if (!cache) {
				request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
			}
			return handler;
		}

		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			handler = hm.getHandler(request);
			if (handler != null) {
				if (cache) {
					request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
				}
				return handler;
			}
		}
		return null;
	}

  第二个参数cache可以看出就是设置缓存,为request保存它的HandlerExecutionChain,而整个getHandler方法其实就是遍历ApplicationContext中用到的HandlerMapping,查找匹配到request的handler。

  接着往下走,看到了如果遍历都没有查找出handler,那么就得进入下面的方法了:

noHandlerFound(processedRequest, response);

  它的主体:

/**
	 * No handler found -> set appropriate HTTP response status.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @throws Exception if preparing the response failed
	 */
	protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (pageNotFoundLogger.isWarnEnabled()) {
			String requestUri = new UrlPathHelper().getRequestUri(request);
			pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri +
					"] in DispatcherServlet with name '" + getServletName() + "'");
		}
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
	}

  看一眼就明白就是报错用的。

  继续向下:

// Apply preHandle methods of registered interceptors.
				HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();

 先搁下,因为它涉及的方法还没有研究和理解。以后再看,做任何事都要有主次之分,我们现在是了解流程,知道它就是预处理,那就等后面再看懂这个方法。

  我们再来看它获取Adapter的过程:

/**
	 * Return the HandlerAdapter for this handler object.
	 * @param handler the handler object to find an adapter for
	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
	 */
	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 +
				"]: Does your handler implement a supported interface like Controller?");
	}

 又是一个遍历的过程,为了找寻支持handler的Adapter,handler我们暂且以Controller为例来说明,当看到这里的代码时,即使你在前面有所疑问,那也拨开云雾看到晴天了。我们到目前为之所做的工作就是为request,例如"/login"找到能够处理它的Controller,而这个Controller怎么来Adapter制造出来的。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

  这里从字面,我们能想象和理解得到,这已经经过了Controller处理的request,返回了ModelandView对象,

// Do we need view name translation?
				if (mv != null && !mv.hasView()) {//注意这里是没有视图的时候
					mv.setViewName(getDefaultViewName(request));
				}

 这段代码又是干什么呢?我也晕,因为看了一遍它的调用和初始化之后,不是太明白,不急慢慢来分析。

/**
	 * Translate the supplied request into a default view name.
	 * @param request current HTTP servlet request
	 * @return the view name (or <code>null</code> if no default found)
	 * @throws Exception if view name translation failed
	 */
	protected String getDefaultViewName(HttpServletRequest request) throws Exception {
		return this.viewNameTranslator.getViewName(request);
	}

 调用的是this.viewNameTranslator的方法,OK,真麻烦的过程,那咱就去看

viewNameTranslator
/** RequestToViewNameTranslator used by this servlet */
	private RequestToViewNameTranslator viewNameTranslator;

 还是不懂它是干嘛用的,从字面意思看起来像是对请求到视图名这一过程的分析转换,唉,那就去看它的初始化。

/**
	 * Initialize the RequestToViewNameTranslator used by this servlet instance.
	 * <p>If no implementation is configured then we default to DefaultRequestToViewNameTranslator.
	 */
	private void initRequestToViewNameTranslator(ApplicationContext context) {
		try {
			this.viewNameTranslator =
					context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Using RequestToViewNameTranslator [" + this.viewNameTranslator + "]");
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// We need to use the default.
			this.viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class);
			if (logger.isDebugEnabled()) {
				logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
						REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this.viewNameTranslator +
						"]");
			}
		}
	}

 初始化的过程就是一个简单的读取ApplicationContext判断是否有注册

RequestToViewNameTranslator

  如果我猜的没错的话,它其实是匹配request处理后一个自动给予的视图名

this.viewNameTranslator.getViewName(request);

  这个方法就是具体的实现了,但这不是我们本文讨论的内容,本文就是带着走DispatcherServlet的流程。

 好的,继续往下看,累了吧,那喝一杯Spring MVC 源码学习札记(一)DispatcherServlet阅读,咱歇会儿

// Did the handler return a view to render?
			if (mv != null && !mv.wasCleared()) {
				render(mv, processedRequest, 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");
				}
			}

  看来到render了,那就转去看render吧

/**
	 * Render the given ModelAndView.
	 * <p>This is the last stage in handling a request. It may involve resolving the view by name.
	 * @param mv the ModelAndView to render
	 * @param request current HTTP servlet request
	 * @param response current HTTP servlet response
	 * @throws Exception if there's a problem rendering the view
	 */
	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()) {//这是ModelandView的方法用于判断它的属性Object view是否是一个字符串
			// 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() + "'");
		}
		view.render(mv.getModelInternal(), request, response);
	}
public boolean isReference() {
		return (this.view instanceof String);
	}
 

首先是解析viewName,这里大家再熟悉不过了,肯定是ViewResolver的事情了

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

我们用初始化的viewResolvers根据我们的排序顺序,按优先级去查找它们对应前缀后缀的View

如果不是字符串呢,那Object view就已经是一个View对象了,则不需要ViewResolver来解析了。

到此,我们整个流程应该算是马马虎虎的看懂了,完成了,大家也看到了,在其中我们跳过了HandlerInceptor的预处理和后处理方法, View的render方法,HandlerAdapter的handle方法。

以后再了解这些类吧,了解他们的时候就能更好的看清Spring mvc的组织结构,这种看代码的方法其实挺舒服的,等于是我用了过程式的方法。

相关推荐

TiDBPingCAP / 0评论 2020-07-29