【SpringMVC框架】前端控制器源代码分析-阿里云开发者社区

开发者社区> 开发与运维> 正文

【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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章