博客
关于我
《深入分析javaweb技术内幕》之一——SpringMVC的工作机制与设计模式
阅读量:134 次
发布时间:2019-02-27

本文共 12873 字,大约阅读时间需要 42 分钟。

一、springMVC

1.1 springMVC的总体设计

springMVC源码下载:http://download.csdn.net/detail/yangchao13341408947/8905157

要使用springMVC,需要:

1) 在web.xml文件中配置一个DispatcherServlet,如下<servlet>

springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-servlet.xml
2
springmvc
/
2) 定义一个springmvc-servlet.xml,定义Mapping、view、control

<!--view-->

<!--使用注解方式Mapping\control-->

总结:springMVC的使用非常简单,我们只要扩展一个路径映射关系;定义一个试图解析器;再定义一个业务逻辑的处理流程规则,springMVC 就能够帮你完成所有的MVC功能了。

要搞清楚springMVC如何工作,主要看DispatcherServlet的代码与DispatcherServlet类相关的结构。

1.2 DispatcherServlet

1.2.1 DispatcherServlet 类图

DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet(-->表示继承)

DispatcherServlet 类继承了HttpServlet,在servlet的init方法调用时DispatcherServlet执行springMVC的初始化工作。可以在其initStrategies方法中知道:

protected void initStrategies(ApplicationContext context) {		initMultipartResolver(context);		initLocaleResolver(context);		initThemeResolver(context);		initHandlerMappings(context);		initHandlerAdapters(context);		initHandlerExceptionResolvers(context);		initRequestToViewNameTranslator(context);		initViewResolvers(context);		initFlashMapManager(context);	}

1) initMutipartResolver  用于处理文件上传

2)initLocaleResolver  用于处理国际化

3)initThemeResolver 用于定义一个主题

4) initHandlerMappings 定义用户设置的请求映射关系

5) initHandlerAdapters 根据handler的类型定义不同的处理规则

6)initHandlerExceptionResolvers 当handler出错时,会通过这个handler统一处理

7)initRequestToViewNameTranslator 将指定的viewname按照定义的requestToViewNameTranslator替换成想要的格式,如加上前缀或者后缀

8)initViewResolvers 将view解析成页面

9)initFlashMapManager :Initialize the {@link FlashMapManager} used by this servlet instance.

1.2.2 DispatcherServlet启动都做了什么

在springMVC框架中,有3个组件是用户必须要定义和扩展的:定义URL映射规则、实现业务逻辑的handler实例对象、渲染模板资源。

HttpServlet 初始化时调用了HttpServletBean 的init方法:该方法的作用是获取servlet中的init参数,并创建一个beanwrapper对象,然后由子类真正执行beanwrapper的初始化工作,代码如下:

@Override	public final void init() throws ServletException {		if (logger.isDebugEnabled()) {			logger.debug("Initializing servlet '" + getServletName() + "'");		}		// Set bean properties from init parameters.		try {			PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));			initBeanWrapper(bw);			bw.setPropertyValues(pvs, true);		}		catch (BeansException ex) {			logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);			throw ex;		}		// Let subclasses do whatever initialization they like.		initServletBean();		if (logger.isDebugEnabled()) {			logger.debug("Servlet '" + getServletName() + "' configured successfully");		}	}
但是HttpServletBean和DispatcherServlet都没有覆盖其initBeanWrapper(bw)方法,所以创建的BeanWrapper对象没有任何作用,spring容器也不是通过BeanWrapper创建的。

spring容器的创建是在FrameworkServlet类中的initServletBean()方法中完成的,这个方法会创建WebApplicationContext 对象。

@Override	protected final void initServletBean() throws ServletException {		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");		if (this.logger.isInfoEnabled()) {			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");		}		long startTime = System.currentTimeMillis();		try {			this.webApplicationContext = initWebApplicationContext();			initFrameworkServlet();		}		catch (ServletException ex) {			this.logger.error("Context initialization failed", ex);			throw ex;		}		catch (RuntimeException ex) {			this.logger.error("Context initialization failed", ex);			throw ex;		}		if (this.logger.isInfoEnabled()) {			long elapsedTime = System.currentTimeMillis() - startTime;			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +					elapsedTime + " ms");		}	}

spring容器的加载时会调用DispatcherServlet的initStrategies()方法来完成DispatcherServlet中定义的初始化工作。在initStrategies()方法中会初始化springMVC框架需要的8个组件,这个8个组件对应的8个bean对象都保存在DispatcherServlet类中。

1.3 control设计

springMVC的control主要由HandlerMapping和HandlerAdapters两个组件提供。HandlerMapping负责映射用户的URL和对应的处理类,HandlerAdapters并没有规定这个URL与应用的处理类如何映射,在HandlerMapping接口中只定义了根据一个URL必须返回一个由HandlerExecutionChain代表的处理链,我们可以在这个处理链中添加任意的HandlerAdapters实例来处理这个URL请求。

1.3.1 HandlerMapping 初始化

Spring MVC 本身提供了很多HandlerMapping的实现类,默认使用BeanNameUrlHandlerMapping。以SimpleUrlHandlerMapping为例看Spring MVC如何将请求的URL映射到我们定义的bean的。

package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;public interface HandlerMapping {	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";		String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";	String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;}

package org.springframework.web.servlet.handler;import java.util.HashMap;import java.util.Map;import java.util.Properties;import org.springframework.beans.BeansException;import org.springframework.util.CollectionUtils;public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {	private final Map
urlMap = new HashMap
(); public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } public void setUrlMap(Map
urlMap) { this.urlMap.putAll(urlMap); } public Map
getUrlMap() { return this.urlMap; } @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } protected void registerHandlers(Map
urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { for (Map.Entry
entry : urlMap.entrySet()) { String url = entry.getKey(); Object handler = entry.getValue(); // Prepend with slash if not already present. if (!url.startsWith("/")) { url = "/" + url; } // Remove whitespace from handler bean name. if (handler instanceof String) { handler = ((String) handler).trim(); } registerHandler(url, handler); } } }}

1.3.2 HandlerAdapter 初始化

package org.springframework.web.servlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public interface HandlerAdapter {	boolean supports(Object handler);	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;	long getLastModified(HttpServletRequest request, Object handler);}

HandlerMapping可以完成URL和Handler(思考:这里的Handler是什么?)的映射关系,那么HandlerAdapter就可以帮助自定义各种Handler了。在springMVC中提供的三种典型的HandlerMapping实现类。

SimpleServletHandlerAdapter :可以继承servlet接口

HttpRequestHandlerAdapter:可以继承HttpRequestHandler接口

SimpleControllerHandlerAdapter:可以继承controller接口

HandlerAdapter 的初始化:简单的创建一个HandlerAdapter对象,将这个对象保存在DispatcherServlet的HandlerAdapters集合中。当springMVC将某个URL对应到某个Handler时,在HandlerAdapters集合中查询那个HandlerAdapter接口对应的方法。

1.3.3 control的调用逻辑

整个springMVC的调用是从DispatcherServlet的doService()方法开始的,在doService方法中会将ApplicationContext、localResolver、themeResolver等对象添加到request中以便于在后面使用。接着调用doDispatch方法,这个方法是主要的处理用户请求的地方。

@Override	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {		if (logger.isDebugEnabled()) {			String requestUri = urlPathHelper.getRequestUri(request);			String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";			logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +					" processing " + request.getMethod() + " request for [" + requestUri + "]");		}		// Keep a snapshot of the request attributes in case of an include,		// to be able to restore the original attributes after the include.		Map
attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { logger.debug("Taking snapshot of request attributes before include"); attributesSnapshot = new HashMap
(); Enumeration
attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); try { doDispatch(request, response); } finally { if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
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);				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);			}		}	}
control处理的关键是DispatcherServlet的handlerMappings集合中根据请求的URL匹配每个HandlerMapping对象中的某个Handler,匹配成功返回这个Handler的处理链HandlerExecutionChain对象,这个HandlerExecutionChain对象中包含了多个HandlerInterceptor对象。HandlerInterceptor接口中定义的三个方法:preHandle和postHandle分别在Handler执行前和执行后执行,afterCompletion在view渲染完成、DispatcherServlet返回之前执行。

Q1:HandlerExecutionChain

参考:《深入分析javaweb技术内幕》——许立波,电子工业出版社

你可能感兴趣的文章
NIFI分页获取Mysql数据_导入到Hbase中_并可通过phoenix客户端查询_含金量很高的一篇_搞了好久_实际操作05---大数据之Nifi工作笔记0045
查看>>
NIFI同步MySql数据_到SqlServer_错误_驱动程序无法通过使用安全套接字层(SSL)加密与SQL Server_Navicat连接SqlServer---大数据之Nifi工作笔记0047
查看>>
Nifi同步过程中报错create_time字段找不到_实际目标表和源表中没有这个字段---大数据之Nifi工作笔记0066
查看>>
NIFI大数据进阶_FlowFile拓扑_对FlowFile内容和属性的修改删除添加_介绍和描述_以及实际操作---大数据之Nifi工作笔记0023
查看>>
NIFI大数据进阶_Json内容转换为Hive支持的文本格式_操作方法说明_01_EvaluteJsonPath处理器---大数据之Nifi工作笔记0031
查看>>
NIFI大数据进阶_Kafka使用相关说明_实际操作Kafka消费者处理器_来消费kafka数据---大数据之Nifi工作笔记0037
查看>>
NIFI大数据进阶_Kafka使用相关说明_实际操作Kafka生产者---大数据之Nifi工作笔记0036
查看>>
NIFI大数据进阶_NIFI的模板和组的使用-介绍和实际操作_创建组_嵌套组_模板创建下载_导入---大数据之Nifi工作笔记0022
查看>>
NIFI大数据进阶_NIFI监控的强大功能介绍_处理器面板_进程组面板_summary监控_data_provenance事件源---大数据之Nifi工作笔记0025
查看>>
NIFI大数据进阶_NIFI集群知识点_认识NIFI集群以及集群的组成部分---大数据之Nifi工作笔记0014
查看>>
NIFI大数据进阶_NIFI集群知识点_集群的断开_重连_退役_卸载_总结---大数据之Nifi工作笔记0018
查看>>
NIFI大数据进阶_内嵌ZK模式集群1_搭建过程说明---大数据之Nifi工作笔记0015
查看>>
NIFI大数据进阶_外部ZK模式集群1_实际操作搭建NIFI外部ZK模式集群---大数据之Nifi工作笔记0017
查看>>
NIFI大数据进阶_实时同步MySql的数据到Hive中去_可增量同步_实时监控MySql数据库变化_操作方法说明_01---大数据之Nifi工作笔记0033
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_01_实际操作---大数据之Nifi工作笔记0029
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_02_实际操作_splitjson处理器_puthdfs处理器_querydatabasetable处理器---大数据之Nifi工作笔记0030
查看>>
NIFI大数据进阶_离线同步MySql数据到HDFS_说明操作步骤---大数据之Nifi工作笔记0028
查看>>
NIFI大数据进阶_连接与关系_设置数据流负载均衡_设置背压_设置展现弯曲_介绍以及实际操作---大数据之Nifi工作笔记0027
查看>>
NIFI数据库同步_多表_特定表同时同步_实际操作_MySqlToMysql_可推广到其他数据库_Postgresql_Hbase_SqlServer等----大数据之Nifi工作笔记0053
查看>>
NIFI汉化_替换logo_二次开发_Idea编译NIFI最新源码_详细过程记录_全解析_Maven编译NIFI避坑指南001---大数据之Nifi工作笔记0068
查看>>