`
sha0k
  • 浏览: 83973 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

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

 
阅读更多

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

 

springmvc

请求进来后必须经由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的组织结构,这种看代码的方法其实挺舒服的,等于是我用了过程式的方法。

 

 

 

 

 

 

0
2
分享到:
评论

相关推荐

    Spring源码学习九:DispatcherServlet初始化源码分析1

    Spring源码学习九:DispatcherServlet初始化源码分析1

    spring MVC 配置文档

    DispatcherServlet 是Spring MVC 的入口 所有进入Spring Web 的 Request 都经过 DispatcherServlet 需要在 web.xml 中注册 DispatcherServlet &lt;servlet&gt; &lt;servlet-name&gt;dispatherContext&lt;/servlet-name&gt; ...

    Spring MVC入门教程

    四、spring mvc DispatcherServlet说明 五、spring mvc 双亲上下文的说明 六、springMVC-mvc.xml 配置文件片段讲解 七、spring mvc 如何访问到静态的文件,如jpg,js,css? 八、spring mvc 请求如何映射到具体的...

    Spring MVC 教程 快速入门 深入分析

    四、spring mvc DispatcherServlet说明 五、spring mvc 双亲上下文的说明 六、springMVC-mvc.xml 配置文件片段讲解 七、spring mvc 如何访问到静态的文件,如jpg,js,css? 八、spring mvc 请求如何映射到具体的...

    Spring MVC 入门实例

    如果你手上有一本《Spring in Action》, 那么你最好从第三部分"Spring 在 Web 层的应用--建立 Web 层"开始看, 否则那将是一场恶梦! 首先, 我需要在你心里建立起 Spring MVC 的基本概念. 基于 Spring 的 Web 应用...

    Spring源码学习十:DispatcherServlet请求分发源码分析1

    总结:首先,SpringMVC框架在启动的时候会遍历Spring容器中的所有bean,对标注了@Controller或并,使用@RequestMapping注解

    SpringMVC框架架构介绍

    四、spring mvc DispatcherServlet说明 五、spring mvc 双亲上下文的说明 六、springMVC-mvc.xml 配置文件片段讲解 七、spring mvc 如何访问到静态的文件,如jpg,js,css? 八、spring mvc 请求如何映射到具体的...

    spring-webmvc5.3.6 jar包.rar

    spring-webmvc 是 Spring MVC 的一个实现。spriing-webmvc 依赖于 spring-web如果直接使用spring-webmvc,就会隐式地添加 spring-web。不必显示添加 spring-web。 该jar包含Spring MVC框架相关的所有类,如Servlets,...

    spring的jar包解决:DispatcherServlet

    dist下的modules下的jar包挨个试的,最后确定三个:(dist下)spring.jar,(dist/modules/)spring-webmvc.jar,及从另外找(下的)的 commons-logging.jar包;而且在Eclipse从build path------configure build path里面...

    java spring mvc

    首先客户端发出spring mvc请求,请求到达DispatcherServlet主控制器处理(前端控制器) b.主控制器调用HandlerMapping组件,根据请求不同调用Controller处理器 c.主控制器调用Controller方法处理请求,(对DB操作可以...

    spring_MVC源码

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,...

    全面掌握Spring MVC:从基础到高级的实践指南

    本文通过分析Spring MVC的核心组件和执行流程,提供了一个全面的学习指南。 Spring MVC基于Model-View-Controller(MVC)架构模式,优化了Web应用程序的设计和开发。在Spring MVC中,DispatcherServlet作为前端控制...

    Pro Spring MVC With Web Flow

    What you'll learn Key Spring Framework fundamentals How to use the Spring MVC architecture How to develop with the DispatcherServlet How to write Controllers How to work with Resolving and ...

    Spring MVC简介.docx

    Spring MVC的设计是围绕DispatcherServlet展开的,DispatcherServlet负责将请求派发到特定的handler。通过可配置的handler mappings、view resolution、locale以及theme resolution来处理请求并且转到对应的视图。

    spring MVC架构

    Spring MVC 是一个模型 - 视图 - 控制器(MVC)的Web框架建立在中央前端控制器servlet(DispatcherServlet),它负责发送每个请求到合适的处理程序,使用视图来最终返回响应结果的概念。Spring MVC 是 Spring 产品...

    spring mvc 3.2 参考文档

    Spring Web model-view-controller (MVC)框架是围绕 DispatcherServlet 设计的,并分发请求到处理程序(handler),Spring MVC支持可配置的处理程序映射(handler mapping),视图解析(view resolution)、 区域设置...

    Spring MVC学习(三)

    Spring MVC学习(三)-------DispatcherServlet详解

    Spring MVC学习之DispatcherServlet请求处理详析

    主要给大家介绍了关于Spring MVC学习教程之DispatcherServlet请求处理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    spring MVC HelloWorld

    我自己写的Spring MVC的一个HelloWold,从建HelloWorld工程,搭建环境Install spring,配置web.xml,配置SpringMVC的配置文件springDispatcherServlet-servlet.xml,添加控制器类,建jsp页面,部署 Tomcat,最后运行...

    Spring MVC之DispatcherServlet详解_动力节点Java学院整理

    DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。 具体请参考第二章的图2-1。  ...

Global site tag (gtag.js) - Google Analytics