【SpringMVC框架】前端控制器源代码分析

前端控制器源代码分析

虽然前面讲了一些springmvc的入门程序和配置文件中映射器和适配器的配置,但是我们作为编程人员,了解框架的部分源码还是有必要的,比如前端控制器,它是如何通过Servlet的web.xml配置文件实现拦截并跳转至DispatcherServlet的呢?下面我们详细探讨

众多周知我们的入门程序的web.xml是这么配置的

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<!-- SpringMvc前端控制器 -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器,适配器等)
		如果不配置contextConfigLoaction,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml)
		-->
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	</servlet>

	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<!-- 第一种:*.action。访问以.action结尾由DispatcherServlet进行解析;
		 第二种:/,所有访问的地址都由DispatcherServlet进行解析,对于静态文件的解析,
		 我们要配置不让DispatcherServlet进行解析。使用此种方法可以实现RESTful风格的url;
		 第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时,仍然会由
		 DispatcherServlet进行解析jsp地址,它不能根据jsp页面找到Handler,会报错-->
		<url-pattern>*.action</url-pattern>
	</servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

还记不记得springmvc的执行过程:

图-1.c.springmvc框架

通过前端控制器源码分析springmvc的执行过程。

我们点开org.springframework.web.servlet.DispatcherServlet看看,里面有一个doDiapatch的方法:

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 {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;

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

				// Determine handler adapter for the current request.
				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 (logger.isDebugEnabled()) {
						String requestUri = urlPathHelper.getRequestUri(request);
						logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}

我们来分析这个方法
第一步:前端控制器接收请求

.action类型的URL通过过滤器进入DispatcherServlet类,调用其doDiapatch()方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
                ......
}

第二步:前端控制器调用处理器映射器查找 Handler
在doDiapatch()方法中调用了DispatcherServlet类的getHandler方法

HandlerExecutionChain mappedHandler = null;
......
mappedHandler = getHandler(processedRequest, false);

其中getHandler方法:

@Deprecated
	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
		return getHandler(request);
	}

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

说明映射器根据request当中的URL,找到了Handler,最终返回一个执行器的链(HandlerExecutionChain)。这个链里面有Handler。

第三步:调用处理器适配器执行Handler,得到执行结果ModelAndView

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

第四步:视图渲染,将model数据填充到request域。

视图解析,得到view:
在doDiapatch()方法中有这一句

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

其中processDispatchResult方法

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

		boolean errorView = false;

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

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

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}

其中render(mv, request, response);方法中有

view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);

渲染方法:

view.render(mv.getModelInternal(), request, response);

调用view的渲染方法,将model数据填充到request域

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
		//遍历model里面的数据,填充到request域
		for (Map.Entry<String, Object> entry : model.entrySet()) {
			String modelName = entry.getKey();
			Object modelValue = entry.getValue();
			if (modelValue != null) {
				request.setAttribute(modelName, modelValue);
				if (logger.isDebugEnabled()) {
					logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
							"] to request in view with name '" + getBeanName() + "'");
				}
			}
			else {
				request.removeAttribute(modelName);
				if (logger.isDebugEnabled()) {
					logger.debug("Removed model object '" + modelName +
							"' from request in view with name '" + getBeanName() + "'");
				}
			}
		}
	}

大致了解了源码,便于更好的理解框架。
转载请注明出处:http://blog.csdn.net/acmman/article/details/46980497

时间: 2024-11-02 12:40:22

【SpringMVC框架】前端控制器源代码分析的相关文章

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析_Android

        在前面几篇文章中,我们详细介绍了Android系统进程间通信机制Binder的原理,并且深入分析了系统提供的Binder运行库和驱动程序的源代码.细心的读者会发现,这几篇文章分析的Binder接口都是基于C/C++语言来实现的,但是我们在编写应用程序都是基于Java语言的,那么,我们如何使用Java语言来使用系统的Binder机制来进行进程间通信呢?这就是本文要介绍的Android系统应用程序框架层的用Java语言来实现的Binder接口了.        熟悉Android系统

Android应用程序框架层和系统运行库层日志系统源代码分析

    在开发Android应用程序时,少不了使用Log来监控和调试程序的执行.在上一篇文章Android日志系统驱动程序Logger源代码分析中,我们分析了驱动程序Logger的源代码,在前面的文章浅谈Android系统开发中Log的使用一文,我们也简单介绍在应用程序中使Log的方法,在这篇文章中,我们将详细介绍Android应用程序框架层和系统运行库存层日志系统的源代码,使得我们可以更好地理解Android的日志系统的实现.         我们在Android应用程序,一般是调用应用程序框

【SpringMVC框架】springmvc的基础知识

1springmvc框架 1.1什么是springmvc springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合. springmvc是一个基于mvc的web框架. 如图mvc的web框架 1.2mvc在b/s系统 下的应用 mvc是一个设计模式,mvc在b/s系统 下的应用: 如图mvc在bs系统下的应用 1.3springmvc框架 如图springmvc框架 第一步:发起请求到前端控制器(DispatcherServlet) 第二步:前端

MVC架构探究及其源码实现(4)-前端控制器

博学,切问,近思--詹子知 (https://jameszhan.github.io) 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现.这里我们就采用后一种方式来实现我们的MVC框架.   1.配置web.xml,使得我们的前端控制器可以拦截所有符合要求的用户请求,这里我们的前端控制

Zend Framework前端控制器用法示例_php实例

本文实例讲述了Zend Framework前端控制器用法.分享给大家供大家参考,具体如下: 常用方法 1.getInstance() 功能:用于获取前端控制器实例. 代码如下: <?php $front = Zend_Controller_Front::getInstance(); 执行上述代码,将创建一个前端控制器实例. 2.setControllerDirectory() 功能:用于通知分发器到何处查找动作控制器action controller类文件. 3.getControllerDir

Zend Framework框架路由机制代码分析_php实例

本文分析了Zend Framework框架路由机制代码.分享给大家供大家参考,具体如下: 在框架中,有关路由的调用关系为: 1.apache的mod_rewrite模块把请求路由到框架的启动脚本,一般是index.php: 2.前端控制器Zend_Controller_Front通过dispatch函数进行请求分发: 3.路由器Zend_Controller_Router_Rewrite通过route函数处理路由,对路由器中已有的路由规则,按照加入顺序的逆序(类似于栈,后进先出)对每个route

【SpringMVC框架】小结+视图解析器配置前缀和后缀

1.入门程序小结 通过入门程序理解springmvc前端控制器.处理器映射器.处理器适配器.视图解析器用法. 前端控制器配置: 第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析 第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析   使用此种方式可以实现 RESTful风格的url 处理器映射器: 非注解处理器映射器(了解) 注解的处理器映射器(掌握)对

FreeBSD 5.0中强制访问控制机制的使用与源代码分析(2)

本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容.这一部分较系统地对MAC框架及MLS策略的源代码进行分析. 2 MAC框架与MLS策略源代码分析 与本文相关的源代码文件主要有两个,即 /usr/src/sys/kern/kern_mac.c 和 /usr/src/sys/security/mac_mls/mac_mls.c .另外还有一些头文件如mac.h.mac_poli

FreeBSD 5.0中强制访问控制机制的使用与源代码分析(1)

本文主要讲述FreeBSD 5.0操作系统中新增的重要安全机制,即强制访问控制机制(MAC)的使用与源代码分析,主要包括强制访问控制框架及多级安全(MLS)策略两部分内容.这一部分讲述要将MAC框架与MLS策略用起来,应该做的一些工作,以及如何有效使用它们的问题. 强制访问控制(英文缩写MAC)是实现操作系统安全的一个重要的方法,现在几乎所有的安全操作系统都采用强制访问控制作为其核心安全机制之一.强制访问控制是对操作系统的各种客体(如文件.socket.系统FIFO.SCD.IPC等)进行细粒度