SpringMVC与Web解读(二):Dispatcher
来源:互联网 发布:数据库 图标 编辑:程序博客网 时间:2024/06/05 22:44
在上一章中,主要讲了ContextLoaderListener对IoC容器在Web环境中的构建。本章讲另一个重点,DispatcherServlet
1.DispatcherServlet设计概述
DispatcherServlet作为一个前端控制器,所有的请求都要经过它来处理,进行转发、匹配、数据处理后,由页面进行展示。
在对完成ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet。DispatcherServlet会建立自己的上下文(Context)来持有SpringMVC的Bean对象,在建立这个自己持有的IoC容器的时候,会从ServletContext中得到根上下文(Root Context)来作为DispatcherServlet持有的上下文的双亲上下文。这个根上下文在上一章中有提到,是ContextLoader所设置的。
有了这个根上下文,在对自己持有的上下文进行初始化,并保存到ServletContext
中,供以后使用。
1.1 DispatcherServlet类继承
先看类继承图:
DispatcherSerlvet通过继承FrameworkServlet和HttpServletBean而进程了HttpServlet,通过使用Serevlet AP来响应HTTP请求。
1.2 DispatcherServlet处理过程
从改图中,我们可以看出,其处理过程分为两个部分,一个是初始化,另一个则是doGet/doPost,处理请求的过程。doGet/doPost方法在进过Framework的processRequest()方法简单处理后,调用DispatcherServlet的doService方法,这个方法中封装了doDispatcher,这个方法是实现MVC模式的主要部分,也是接下来重点关注的部分。
2.DispatcherServlet的启动和初始化
作为一个Servlet的,开始时,Servlet的init方法被调用,加载Servlet中的设置。接下来,会执行DispatcherServlet持有的IoC容器的初始化过程,在这个初始化过程中,属于DispatcherServlet的上下文被建立起来,并设置为根上下文的子上下文。IoC容器getBean的时候,是会向双亲上下文getBaen。即根上下文的Bean可以被共享。上下文建立之后,和其它IoC容器一样通过refresh
完成初始化。最后,DispatcherServlet给这个自己持有的上下文命名,并把它设置到Web容器的上下文中,这个名称和在web.xml中设置的DispatcherServlet的Servlet名称相关,从而保证了这个上下文在Web环境上下文体系的唯一性。
实现代码逻辑如上所述,这里不贴出来了。
Bean管理
这个时候IoC容器已经建立起来了,这个IoC容器是根上下文的子容器。对于一个Bean,系统首先到根容器查找,找不到之后,才回到DispatcherServlet所管理的IoC容器中去查找,这个是有IoC容器的getBean
决定的。
2.1 Dispatcher Server的初始化
DispatcherServlet持有一个以自己Servlet命名的IoC容器。这个IoC容器是一个WebApplicationContext对象,这个IoC容器建立之后,意味着DispatcherServlet拥有自己的Bean定义空间,这位后来的XML文件来配置MVC中各个Bean创造了条件。自此之后,Web容器相关的加载已经完成。
接下来,是Sping MVC Dispatcher Server的初始化。
其调用关系如下:
其中初始化在initStrategies方法中完成。
其中,initHandlerMappings
的作用是,为HTTP请求找到相应的Conteroller。其代码如下:
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. //找到所有在ApplicationContext中包含双亲上下文中的HandlerMappings。 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values()); // We keep HandlerMappings in sorted order. //排序 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { //根据名称从当前的IoC容器中通过getBean获取handlerMapping try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. //要有至少一个HandlerMapping,如果没有找到,则设置一个默认的。 if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isDebugEnabled()) { logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default"); } }}
3. MVC处理HTTP分发请求
3.1 HandlerMapping的配置和设计原理
HandlerMapping也就是控制URL和相对应的处理方法的地方。在初始化的时候,在上下文环境中已定义的所有HandlerMapping都已经被加载在一个List中,并被排序(为什么排序)。这个List的每一个元素对应着一个具体的HandlerMapping的配置。一般来说,每一个HandlerMapping可以持有一系列从URL请求道Contorller的映射(对应/*等URL匹配规则)。
HandlerMapping的设计图:
其中,HandlerMapping的getHandler方法:
public interface HandlerMapping{ HandlerExecutionChain getHander(HttpServletRequest request);}public class HandlerExecutionChain { private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class); private final Object handler; //HTTP请求对应的Handler private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList; private int interceptorIndex = -1;//下面是一些增强方法 /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute */ public HandlerExecutionChain(Object handler) { this(handler, (HandlerInterceptor[]) null); } /** * Create a new HandlerExecutionChain. * @param handler the handler object to execute * @param interceptors the array of interceptors to apply * (in the given order) before the handler itself executes */ public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) { if (handler instanceof HandlerExecutionChain) { HandlerExecutionChain originalChain = (HandlerExecutionChain) handler; this.handler = originalChain.getHandler(); this.interceptorList = new ArrayList<HandlerInterceptor>(); CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList); CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList); } else { this.handler = handler; this.interceptors = interceptors; } } /** * Return the handler object to execute. * @return the handler object (may be {@code null}) */ public Object getHandler() { return this.handler; } public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList().add(interceptor); } public void addInterceptors(HandlerInterceptor... interceptors) { if (!ObjectUtils.isEmpty(interceptors)) { CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList()); } } private List<HandlerInterceptor> initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); if (this.interceptors != null) { // An interceptor array specified through the constructor CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList); } } this.interceptors = null; return this.interceptorList; } /** * Return the array of interceptors to apply (in the given order). * @return the array of HandlerInterceptors instances (may be {@code null}) */ public HandlerInterceptor[] getInterceptors() { if (this.interceptors == null && this.interceptorList != null) { this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]); } return this.interceptors; } /** * Apply preHandle methods of registered interceptors. * @return {@code true} if the execution chain should proceed with the * next interceptor or the handler itself. Else, DispatcherServlet assumes * that this interceptor has already dealt with the response itself. */ boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; } /** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle invocation * has successfully completed and returned true. */ void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } /** * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors. */ void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { if (interceptors[i] instanceof AsyncHandlerInterceptor) { try { AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i]; asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler); } catch (Throwable ex) { logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex); } } } } } /** * Delegates to the handler's {@code toString()}. */ @Override public String toString() { Object handler = getHandler(); if (handler == null) { return "HandlerExecutionChain with no handler"; } StringBuilder sb = new StringBuilder(); sb.append("HandlerExecutionChain with handler [").append(handler).append("]"); HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { sb.append(" and ").append(interceptors.length).append(" interceptor"); if (interceptors.length > 1) { sb.append("s"); } } return sb.toString(); }}}
在HandlerExecutionChain中设置了一个拦截器链,通过这个拦截器链中的拦截器,为handler对象提供功能的增强。
以SimpleURLHandlerMapping ,要做的就是根据URL映射的方式,注册Handle和Interceptor(拦截器),从而维护这样的一个映射的Map。
那么,SimplerURLHandlerMapping的注册Handler的过程就是我们接下来要关注的。
3.2 SimpleURLHandlerMapping注册Handler
···
public void initApplicationContext(){
super.initApplicationContext();
registerHandlers(this.urlMap);
}
//表层注册函数
protected void registerHandlers(Map
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // 直接用Bean的名称进行映射 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { //"/"设置到rootHandler中 if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } //"/*"设置到defaultHandler当中 else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } else { //放入到一个Map当中 this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
上面这个就是handlerMap了,其中保存了URL和COnteroller之间的映射关系。
3.3 使用HandlerMapping完成请求的映射处理
这个主要是HandleMapping中定义的getHandler
方法了,只是通过URL匹配规则来获取持有Handler的HandlerExecutionChain。
3.4 SpingMCV对HTTP的请求的分发处理
这个无疑是我们所关注的重点啦。这里重新回到DispatcherSerlvelt。请求的分发在doService
中的doDispatch
完成。
以下是这个方法的过程,看似很长,其实只是,获取HandleExecutinChain,请求前置处理,处理,后置处理,视图呈现,结束。
以下是处理过程图:
以下是Spring技术内幕的代码注释图,不关注View的部分。
接下来,看下DispatcherServlet是如何获得一个Handler的,一个个找.
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; }
视图呈现,在这里不关注。
- SpringMVC与Web解读(二):Dispatcher
- SpringMVC与Web解读(一):SpringMVC环境构建与ContextLoaderListener
- SpringMvc-Dispatcher
- SpringMVC之web.xml解读
- springmvc + hibernate构建web工程(二)
- SpringMVC之Web定时器(二)
- (二)springmvc--构建一个springmvc web项目
- SpringMVC—Dispatcher配置
- SpringMVC 4.2.2 - Web.xml,Dispatcher-Servlet及ApplicationContext配置笔记
- SAP Web Dispatcher
- struts2 与springmvc深入对比(二)
- Springmvc 与 MyBatis的整合(二)
- Springmvc之dispatcher-servlet.xml
- HashMap与HashTable解读(二)
- web.xml配置ActionContextCleanUp (org.apache.struts2.dispatcher.ActionContextCleanUp)
- Caffe解读(二)
- apache ftpServer源码解读与收获(二)
- MVP模式解读与实战运用(二)
- 第一种操作数据库方式
- 《大型分布式网站架构》
- LDA主题模型
- 基于canal数据加工系统
- Spring Boot
- SpringMVC与Web解读(二):Dispatcher
- 【转载】从长度为n的数组中选择m个数的所有结果
- C语言编程预备知识
- Spring在maven中的全依赖
- codeforces 888G.Xor-MST(01字典树+贪心+最小异或生成树)
- canvas相关API
- Deep learning REVIEW--三位大牛的Nature
- 动态内存,智能指针
- Codeforces Round #445 C. Petya and Catacombs 贪心