- 浏览: 84998 次
- 性别:
- 来自: 西安
最新评论
-
w67856101:
sssss
在Spring中使用jcaptcha实现图片验证 -
yjq8116:
不错。很有收获
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4) -
rolenz:
mongodb越来越火,但是网上的资料不是很全,希望深入的写些 ...
MongoDB学习(二)索引的用法 -
sha0k:
rolenz 写道能不能说说replSet到底如何进行认证我才 ...
MongoDB学习(一)安装和基本使用 -
rolenz:
能不能说说replSet到底如何进行认证
MongoDB学习(一)安装和基本使用
Spring MVC 源码学习札记(一)DispatcherServlet阅读
- 博客分类:
- Spring MVC
马上过年了~闲来无事,阅读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是否是multipartrequest,众所周知MultipartResolver在Spring中用来处理文件上传,可参见http://zachary-guo.iteye.com/blog/1294443 这篇博文。
首先进行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的组织结构,这种看代码的方法其实挺舒服的,等于是我用了过程式的方法。
发表评论
-
SpringMVC源码学习札记(六)DispatcherServlet类在3.2中的变化
2013-08-07 22:48 2263protected void doDispatch(Htt ... -
Spring MVC 源码学习札记(五)说说ReflectionUtils
2012-01-30 21:33 7823这里主要是为了解决我前面遇到的问题,因为在默认注解Defaul ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4)
2012-01-30 22:55 8195每天敦促自己阅读spring源码 绝不松懈 下面是当没有注册 ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(3)
2012-01-29 13:44 3885上一节主要阅读了BeanNameUrlHandlerMappi ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(2)
2012-01-28 17:55 3931上一节我们看到 当***-serlvet.xml中未声明Ha ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(1)
2012-01-28 13:35 7630在分析HandlerMapping和HandlerAdapte ... -
Spring MVC 源码学习札记(三)看看View怎么做到视图转发的
2012-01-21 00:16 3238上一节看完了ViewResolver解析视图名产生View的过 ... -
Spring MVC 源码学习札记(二)ViewResolver处理viewname
2012-01-19 17:53 6648第一节我们看到了ViewResolver对ModelAndVi ...
相关推荐
Spring MVC是Java EE开发中广泛使用的一个Web框架,它是Spring框架的一部分,主要负责Model-View-Controller(MVC)架构模式的实现。本知识点将围绕Spring MVC的源码深度剖析展开,详细介绍Spring MVC的源码结构、...
标题中的"开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp"表明这是一个关于Spring MVC框架的开发教程,其中包含了源代码供学习者参考。Spring MVC是Spring框架的一个核心组件...
总的来说,"Mastering Spring MVC 4(2015.09)源码"提供了深入学习Spring MVC的机会,你可以通过阅读和分析源码来了解如何配置DispatcherServlet、怎样编写控制器、如何进行数据绑定与验证,以及如何利用拦截器等特性...
【Spring5MVC源码分析】 Spring MVC 是一个基于Java的、用于构建Web应用程序的、高度可插拔的MVC框架,它是Spring Framework的重要组成部分。Spring MVC的核心目标是简化前端控制器的开发,使得开发者可以专注于...
总的来说,Spring MVC的DispatcherServlet通过一系列步骤,从初始化时的bean扫描到请求到达时的分发处理,确保了请求能够准确地传递到正确的Controller方法。这一过程涉及到bean的扫描、请求映射的建立、拦截器的...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建Web应用程序提供模型-视图-控制器(MVC)架构。Spring MVC的核心是解耦应用程序的各个组件,使其能够独立工作并易于测试。在本源码分析中,我们将探讨Spring...
总的来说,《精通Spring MVC 4》源码是一个深度学习Spring MVC的绝佳资源。通过仔细阅读和分析这些源代码,开发者不仅可以提升对Spring MVC框架的理解,还能学习到设计模式、性能优化和最佳实践,从而在实际项目中更...
- 学习Spring MVC和MyBatis的整合,可以参考官方文档、教程和开源项目的源码。 - 通过调试源码,理解其实现原理,有助于提升实战技能。 总结,这个压缩包提供了一个完整的Spring MVC和MyBatis整合的示例,包含了...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为开发者提供了模型-视图-控制器(MVC)架构,使开发人员能够更好地组织和分离应用程序的业务逻辑、数据处理和用户界面。Spring MVC是Spring框架的一个核心组件,...
在Spring MVC中,每个请求都会经过DispatcherServlet,这是一个前端控制器,它会根据请求的URL和配置的映射规则将请求分发到合适的处理器。处理器可以是自定义的Controller类,Controller通过方法注解(如@...
Spring源码学习九:DispatcherServlet初始化源码分析1 DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请求的入口,本篇将深入源码分析它的初始化过程。 首先,从DispatcherServlet的名称上...
Spring MVC 是一款强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它作为Spring框架的一部分,提供了一种优雅的方式来处理HTTP请求和响应,使得开发者可以专注于业务逻辑而不是底层实现。在这...
### Spring MVC 学习指南详解 #### 一、Spring MVC 概述 Spring MVC 是 Spring 框架的一部分,主要用于构建基于 Model-View-Controller (MVC) 设计模式的 Web 应用程序。它提供了丰富的功能来简化 Web 开发过程,...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个"Spring MVC 基础实例源码01"的资源很可能是为了帮助初学者理解Spring MVC的核心概念和...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。在本实例中,我们将探讨如何利用Spring MVC的注解来简化前后台交互,以及如何实现简单视图...
Spring MVC是Spring框架的一个核心模块,专为构建Web应用程序而设计。它提供了模型-视图-控制器(MVC)架构,使开发者能够有效地分离业务逻辑、数据处理和用户界面。在"Spring MVC 4.2.3"版本中,我们看到了一系列的...
Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建RESTful应用程序提供了强大的支持。这个源码实例应该是为了帮助开发者深入理解Spring MVC的工作原理和实现机制。下面,我们将详细探讨Spring MVC的关键知识...
通过深入学习Spring MVC的源码,开发者不仅可以更好地理解其内部机制,还能优化自己的代码,提升应用程序的性能和可维护性。这本扫描版书籍的源码分析部分,无疑会帮助读者在实践中应用这些知识,解决实际问题。
在这个学习记录总结中,我们将深入理解Spring MVC的核心概念、主要组件以及其工作流程。 1. Spring MVC 概述 Spring MVC 是Spring框架的一部分,它基于Spring IoC(Inversion of Control,控制反转)容器,简化了...