Spring MVC 源码学习札记(一)DispatcherServlet阅读
马上过年了~闲来无事,阅读Spring源码提升自己,做事要有个流程,大家都知道Spring MVC的请求处理流程
请求进来后必须经由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的流程。
好的,继续往下看,累了吧,那喝一杯,咱歇会儿
// 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的组织结构,这种看代码的方法其实挺舒服的,等于是我用了过程式的方法。