`

深入剖析Spring Web源码(八) - 处理器映射,处理器适配器以及处理器的实现 - 基于简单控制器流程的实现

 
阅读更多

 

1.1.1.1                   基于简单控制器流程的实现

简单控制器的流程定义了简单的控制器接口 (Controller),在流程的开始,它通常通过 Bean名字 URL处理器映射(BeanNameUrlHandlerMapping)来获得支持此 HTTP请求的一个控制器实例和支持这个控制器的处理器拦截器(HandlerInterceptor)。通过简单控制处理适配器 (SimpleControllerHandlerAdapter)传递 HTTP请求到简单的控制器( SimpleFormController)来实现 Spring Web MVC的控制流程。这个流程如下图所示,

 

 

图表 4 ‑15

 

首先,我们分析派遣器 Servlet是如何通过 Bean名字 URL处理器映射获得处理器执行链,也就是上图中的第一步。

 

Bean名字 URL处理器映射是通过一些列的父类继承最终实现处理器映射接口的。其中不同的父类抽象出一个独立的类级别,一个类级别完成一个最小化而又完善的功能。下图是它的类继承实现的树结构。

 

 

图表 4 ‑16


上图中灰色类,包括 Web应用程序对象支持类和应用程序对象支持类是 Spring环境项目的实现, Web应用程序对象支持类用来初始化时得到 Web应用程序环境 ,而应用程序对象支持类用来初始化时得到 Servlet环境。这里我们不再剖析他们的实现。

 

抽象处理器映射是这个体系结构中直接实现处理器映射接口的抽象类,这个类继承了 Web应用程序对象支持类,目的是监听 Web应用程序环境初始化事件。在初始化事件中,初始化拦截器,这些拦截器是应用到所有处理器的。如下程序注释,

 

 

[java]  view plain copy
  1. public void setInterceptors(Object[] interceptors) {  
  2.     //通过注入的方式设置通用拦截器,这些拦截器是对象类型,其中真正支持的类型包括HandlerInterceptor和WebRequestInterceptor,这些通用拦截器是应用在所有的处理器上的  
  3.     this.interceptors.addAll(Arrays.asList(interceptors));  
  4. }  
  5. @Override  
  6. protected void initApplicationContext() throws BeansException {  
  7.     //提供占位符方法让子类添加新的拦截器对象  
  8.     extendInterceptors(this.interceptors);  
  9.       
  10.       
  11.     //初始化拦截器,因为拦截器有不同的是是实现,这里需要将不同的拦截器适配到最终的HandlerInterceptor的是实现,这是通过HandlerInterceptorAdapter来实现的  
  12.     initInterceptors();  
  13. }  
  14. protected void initInterceptors() {  
  15.     //如果配置的通用拦截器不为空  
  16.     if (!this.interceptors.isEmpty()) {  
  17.         //对配置的通用拦截器进行适配  
  18.         this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];  
  19.         for (int i = 0; i < this.interceptors.size(); i++) {  
  20.             Object interceptor = this.interceptors.get(i);  
  21.             //对空的拦截器进行校验  
  22.             if (interceptor == null) {  
  23.                 throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");  
  24.             }  
  25.             //对每个拦截器进行适配  
  26.             this.adaptedInterceptors[i] = adaptInterceptor(interceptor);  
  27.         }  
  28.     }  
  29. }  
  30. protected HandlerInterceptor adaptInterceptor(Object interceptor) {  
  31.     if (interceptor instanceof HandlerInterceptor) {  
  32.         //如果拦截器是HandlerInterceptor本身的是实现,不需要适配  
  33.         return (HandlerInterceptor) interceptor;  
  34.     }  
  35.     else if (interceptor instanceof WebRequestInterceptor) {  
  36.         //如果拦截器是WebRequestHandlerInterceptorAdapter,则适配到通用的HandlerInterceptor的实现  
  37.         return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);  
  38.     }  
  39.     else {  
  40.         //不支持其他类型的拦截器  
  41.         throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());  
  42.     }  
  43. }  

 

 

当派遣器 Servlet要求处理器映射翻译一个请求到处理器执行链的时候,抽象处理器则映射一个内部的处理器,连同初始化的拦截器一起构成处理器执行链返回。抽象处理器定义映射内部的处理器逻辑作为一个抽象的方法,子类需要实现这个方法来解析处理器。如下程序注释,

 

 

[java]  view plain copy
  1. //实现处理器映射的方法,这个方法是派遣器Servlet要求翻译HTTP请求到处理器执行链的入口  
  2. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  3.     //使用某种映射逻辑,将请求映射到一个真正的处理器,这个方法定义为抽象的,子类必须实现它,例如,实现基于URL到Beean名称的映射逻辑  
  4.     Object handler = getHandlerInternal(request);  
  5.       
  6.     //如果没有映射的处理器,则使用缺省的处理器  
  7.     if (handler == null) {  
  8.         //子类可以设置缺省的处理器,也可以通过注射的方式设置缺省的处理器  
  9.         handler = getDefaultHandler();  
  10.     }  
  11.       
  12.     //如果没有发现任何处理器,则返回空处理器, 派遣器Servlet将发送HTTP错误响应SC_NOT_FOUND(404)  
  13.     if (handler == null) {  
  14.         return null;  
  15.     }  
  16.       
  17.     //如果内部映射逻辑实现返回一个字符串,则认为这个字符串是Bean的名字  
  18.     if (handler instanceof String) {  
  19.         String handlerName = (String) handler;  
  20.           
  21.         //在应用程序环境中通过Bean名字查找这个Bean  
  22.         handler = getApplicationContext().getBean(handlerName);  
  23.     }  
  24.       
  25.     //连同处理器拦截器一起构造处理器执行链  
  26.     return getHandlerExecutionChain(handler, request);  
  27. }  
  28. //抽象的处理器映射逻辑,这个逻辑的目的是映射一个HTTP请求到一个处理器对象,子类应该根据某些规则进行实现,通常是根据URL匹配Bean的名字来实现的,当然也可以匹配Bean的类或者其他的特征,我们将在第四小结剖析所有子类的实现   
  29. protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;  
  30. protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {  
  31.     //判断处理器对象的类型  
  32.     if (handler instanceof HandlerExecutionChain) {  
  33.         //如果处理器已经是处理器对象  
  34.         HandlerExecutionChain chain = (HandlerExecutionChain) handler;  
  35.           
  36.         //添加初始化的拦截器  
  37.         chain.addInterceptors(getAdaptedInterceptors());  
  38.           
  39.         //返回处理器执行链  
  40.         return chain;  
  41.     }  
  42.     else {  
  43.         //否则使用处理器和初始化的拦截器构造处理器执行链的对象  
  44.         return new HandlerExecutionChain(handler, getAdaptedInterceptors());  
  45.     }  
  46. }  

 

 

我们看见抽象的处理器映射也实现了 Ordered接口,这个接口是用来在有多个处理器映射可供派遣器 Servlet使用时的优先级。

 

类体系结构中的下一个类是抽象 URL处理器映射,正如我们所愿,抽象 URL处理器映射实现了getHandlerInternal()方法,在方法实现里通过请求的 URL匹配相应的映射处理器和映射拦截器来返回处理器执行链对象的。

 

那么,这些映射拦截器和映射处理器是如何进行初始化的呢?映射的拦截器是在初始化的时候,通过在 Web应用程序环境中查找得到的。这些映射拦截器必须是 MappedInterceptor的子类,而且注册在 Web应用程序环境中。它也提供了一个方法 registerHandler(),供子类调用注册响应的处理器。如下程序注释,

 

 

[java]  view plain copy
  1. //改写了抽象处理器的拦截器初始化的方法,初始化更多配置在Web应用程序环境中的映射拦截器,映射拦截器是一个从URL到处理器拦截器对象映射的实现  
  2. @Override  
  3. protected void initInterceptors() {  
  4.     //初始化父类的通用拦截器,这些拦截器应用到所有的处理器上  
  5.     super.initInterceptors();  
  6.       
  7.     //查找所有在Web应用程序环境中注册的MappedInterceptor的实现  
  8.     Map<String, MappedInterceptor> mappedInterceptors = BeanFactoryUtils.beansOfTypeIncludingAncestors(  
  9.             getApplicationContext(), MappedInterceptor.class, true, false);  
  10.       
  11.     //如果找到任何MappedInterceptor的实现  
  12.     if (!mappedInterceptors.isEmpty()) {  
  13.         //构造MappedInterceptor的集合类并且存储,这个集合类提供了通过URL过滤拦截器的功能  
  14.         this.mappedInterceptors = new MappedInterceptors(mappedInterceptors.values().toArray(  
  15.                 new MappedInterceptor[mappedInterceptors.size()]));  
  16.     }  
  17. }  
  18. protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {  
  19.     //注册的处理器必须映射到一个URL路径上  
  20.     Assert.notNull(urlPaths, "URL path array must not be null");  
  21.       
  22.     //对于拥有多个URL的处理器,分别注册URL到处理器的映射  
  23.     for (String urlPath : urlPaths) {  
  24.         registerHandler(urlPath, beanName);  
  25.     }  
  26. }  
  27. //注册一个URL到一个处理器的映射  
  28. protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {  
  29.     //URL和处理器都不能为空  
  30.     Assert.notNull(urlPath, "URL path must not be null");  
  31.     Assert.notNull(handler, "Handler object must not be null");  
  32.       
  33.     //开始解析处理器  
  34.     Object resolvedHandler = handler;  
  35.     //如果没有配置懒惰初始化处理器选项,则把使用的处理器名字转换为Web应用程序环境中的Bean  
  36.     //如果配置懒惰初始化处理器选项,则这个转换是在返回处理器执行链的过程中实现的  
  37.     if (!this.lazyInitHandlers && handler instanceof String) {  
  38.         String handlerName = (String) handler;  
  39.           
  40.         //如果这个Bean使用单例模式,则在初始化的时候进行转换,在后续的服务方法中不再进行转换,提高了效率  
  41.         //但是,如果非单例模式,我们不能在初始化的时候进行转换,例如,如果Bean的范围是Session,服务方法(getHanlderInternal)会的到不同的处理器实例对于不同的Session,在这种情况,如果初始化的时候进行转换,则每次返回同一个Bean,变成了单例模式了  
  42.         if (getApplicationContext().isSingleton(handlerName)) {  
  43.             resolvedHandler = getApplicationContext().getBean(handlerName);  
  44.         }  
  45.     }  
  46.     //查看是否这个URL已经注册过处理器了  
  47.     Object mappedHandler = this.handlerMap.get(urlPath);  
  48.     if (mappedHandler != null) {  
  49.         //如果这个URL确实已经注册过处理器了  
  50.         if (mappedHandler != resolvedHandler) {  
  51.             //抛出异常,提示用户配置错误  
  52.             throw new IllegalStateException(  
  53.                     "Cannot map handler [" + handler + "] to URL path [" + urlPath +  
  54.                     "]: There is already handler [" + resolvedHandler + "] mapped.");  
  55.         }  
  56.     }  
  57.     else {  
  58.         //如果这个URL没有注册过处理器了  
  59.         if (urlPath.equals("/")) {  
  60.             //如果是根处理器  
  61.             if (logger.isInfoEnabled()) {  
  62.                 logger.info("Root mapping to handler [" + resolvedHandler + "]");  
  63.             }  
  64.             //设置根处理器  
  65.             setRootHandler(resolvedHandler);  
  66.         }  
  67.         else if (urlPath.equals("/*")) {  
  68.             //如果是默认处理器  
  69.             if (logger.isInfoEnabled()) {  
  70.                 logger.info("Default mapping to handler [" + resolvedHandler + "]");  
  71.             }  
  72.             //设置默认处理器  
  73.             setDefaultHandler(resolvedHandler);  
  74.         }  
  75.         else {  
  76.             //设置正常的处理器  
  77.             this.handlerMap.put(urlPath, resolvedHandler);  
  78.             if (logger.isInfoEnabled()) {  
  79.                 logger.info("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]");  
  80.             }  
  81.         }  
  82.     }  
  83. }  

 

 

我们可以看到这些映射拦截器是在 Web应用程序环境中查找得到的,他们必须实现 MappedInterceptor接口。得到的这些 MappedInterceptor接口保存在 MappedInterceptors集合类中,这个类同时提供了基于 URL路径过滤的功能。如下程序注释,

 

 

[java]  view plain copy
  1. public Set<HandlerInterceptor> getInterceptors(String lookupPath, PathMatcher pathMatcher) {  
  2.     //构造过滤结果集合  
  3.     Set<HandlerInterceptor> interceptors = new LinkedHashSet<HandlerInterceptor>();  
  4.       
  5.     //遍历所有配置的映射拦截器  
  6.     for (MappedInterceptor interceptor : this.mappedInterceptors) {  
  7.         //如果配置的拦截器匹配此路径  
  8.         if (matches(interceptor, lookupPath, pathMatcher)) {  
  9.             //添加当前的拦截器到过滤结果集合中  
  10.             interceptors.add(interceptor.getInterceptor());               
  11.         }  
  12.     }  
  13.       
  14.     //返回过滤结构  
  15.     return interceptors;  
  16. }  
  17. private boolean matches(MappedInterceptor interceptor, String lookupPath, PathMatcher pathMatcher) {  
  18.     //映射拦截器保存这从URL到处理器拦截器的映射关系,这个方法取得这个处理器映射器支持的所有URL Pattern  
  19.     String[] pathPatterns = interceptor.getPathPatterns();  
  20.       
  21.     //判断是否配置了URL Pattern  
  22.     if (pathPatterns != null) {  
  23.         //如果配置了URL Pattern  
  24.         for (String pattern : pathPatterns)   
  25.             //查看是否存在一个URL Pattern匹配此路径  
  26.             if (pathMatcher.match(pattern, lookupPath)) {  
  27.                 //如果匹配此路径,则使用当前映射拦截器  
  28.                 return true;  
  29.             }  
  30.         }  
  31.         // 如果没有匹配此路径的URL Pattern,则不使用当前映射拦截器  
  32.         return false;  
  33.     } else {  
  34.         //在没有配置任何URL Pattern情况下,这个映射拦截器默认成为通用的拦截器,会被应用到所有的处理器上  
  35.         return true;  
  36.     }  
  37. }  

 

 

抽象 URL处理器映射初始化了拦截器和处理器之后,它将如何实现映射请求到处理器执行链的逻辑呢?正如我们所想,它通过 URL精确匹配或者最佳匹配查找注册的拦截器和处理器,然后构造处理器执行链对象。如下程序注释,

 

 

[java]  view plain copy
  1. //实现抽象处理器映射的抽象发放,提供基于URL匹配的实现  
  2. @Override  
  3. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  
  4.     //通过实用方法获得查找路径,这个查找路径 = URI - "http://" - hostname:port - application context - servlet mapping prefix  
  5.     //例如  http://www.robert.com/jpetstore/petstore/insert/ - "http://" - "www.robert.com" - "jpetstore" - "petstore" = "/insert"  
  6.     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  
  7.       
  8.     //匹配最佳匹配处理器  
  9.     Object handler = lookupHandler(lookupPath, request);  
  10.     if (handler == null) {  
  11.         //如果没有最佳匹配处理器  
  12.         Object rawHandler = null;  
  13.         if ("/".equals(lookupPath)) {  
  14.             //如果查找路径是根路径,则使用根处理器  
  15.             rawHandler = getRootHandler();  
  16.         }  
  17.         if (rawHandler == null) {  
  18.             //否则使用缺省处理器  
  19.             rawHandler = getDefaultHandler();  
  20.         }  
  21.         if (rawHandler != null) {  
  22.             //如果是根路径或者配置了缺省处理器  
  23.               
  24.             if (rawHandler instanceof String) {  
  25.                 //翻译处理器Bean名字到Bean对象本身,如果配置了懒惰加载为false, 而且处理器是单例模式,这个转换在初始化的时候已经做完了  
  26.                 String handlerName = (String) rawHandler;  
  27.                 rawHandler = getApplicationContext().getBean(handlerName);  
  28.             }  
  29.               
  30.             //定义占位符方法校验处理器  
  31.             validateHandler(rawHandler, request);  
  32.               
  33.             //增加新的处理器拦截器导出最佳匹配路径和查找路径,既然我们使用了根处理器或者缺省处理器,这两个值都是查找路径  
  34.             handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);  
  35.         }  
  36.     }  
  37.     if (handler != null && this.mappedInterceptors != null) {  
  38.         //如果存在最佳匹配处理器,过滤映射拦截器,得到所有匹配的处理器拦截器,匹配过程在前文中已经分析  
  39.         Set<HandlerInterceptor> mappedInterceptors =  
  40.                 this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher);  
  41.         if (!mappedInterceptors.isEmpty()) {  
  42.             HandlerExecutionChain chain;  
  43.             if (handler instanceof HandlerExecutionChain) {  
  44.                 //如果处理器拦截器是处理器执行链对象,则使用已存对象  
  45.                 chain = (HandlerExecutionChain) handler;  
  46.             } else {  
  47.                 //否则创建新的处理器执行链  
  48.                 chain = new HandlerExecutionChain(handler);  
  49.             }  
  50.             //添加过滤得到的处理器拦截器到处理器执行链中  
  51.             chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()]));  
  52.         }  
  53.         //【问题】既然方法开始调用了lookupHandler(),那么返回处理器映射器对象或者null,所以创建新的处理器执行链逻辑不会执行。如果子类改写了lookupHandler()返回一个普通的处理器对象,创建新的处理器执行链逻辑不被执行,但是新的处理器执行链对象没有返回,所以,添加的处理器拦截器将会丢失,这里程序设计有些不妥  
  54.     }  
  55.     if (handler != null && logger.isDebugEnabled()) {  
  56.         //记录日志成功的映射了处理器  
  57.         logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'");  
  58.     }  
  59.     else if (handler == null && logger.isTraceEnabled()) {  
  60.         //记录日志映射处理器失败  
  61.         logger.trace("No handler mapping found for [" + lookupPath + "]");  
  62.     }  
  63.       
  64.     //返回处理器,可能为空  
  65.     return handler;  
  66. }  
  67. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {  
  68.     // 首先执行精确匹配,查找路径和处理器配置的URL完全相同  
  69.     Object handler = this.handlerMap.get(urlPath);  
  70.     if (handler != null) {  
  71.         // 精确匹配成功  
  72.         if (handler instanceof String) {  
  73.             //翻译Bean名到Bean对象本身  
  74.             String handlerName = (String) handler;  
  75.             handler = getApplicationContext().getBean(handlerName);  
  76.         }  
  77.         //调用占位符方法校验处理器  
  78.         validateHandler(handler, request);  
  79.           
  80.         //增加新的处理器拦截器导出最佳匹配路径和查找路径,既然精确匹配成功,这两个值都是查找路径  
  81.         return buildPathExposingHandler(handler, urlPath, urlPath, null);  
  82.     }  
  83.     // 执行最佳匹配方案  
  84.     List<String> matchingPatterns = new ArrayList<String>();  
  85.     for (String registeredPattern : this.handlerMap.keySet()) {  
  86.         //取得所有匹配的处理器注册的URL Pattern  
  87.         if (getPathMatcher().match(registeredPattern, urlPath)) {  
  88.             matchingPatterns.add(registeredPattern);  
  89.         }  
  90.     }  
  91.       
  92.     //决定最佳匹配  
  93.     String bestPatternMatch = null;  
  94.     if (!matchingPatterns.isEmpty()) {  
  95.         //对匹配的URL Pattern进行排序  
  96.         Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath));  
  97.         if (logger.isDebugEnabled()) {  
  98.             logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);  
  99.         }  
  100.           
  101.         //排序后数组的第一个匹配为最佳匹配  
  102.         bestPatternMatch = matchingPatterns.get(0);  
  103.     }  
  104.     if (bestPatternMatch != null) {  
  105.         //如果存在最佳匹配,则找到最佳匹配URL Pattern的处理器  
  106.         handler = this.handlerMap.get(bestPatternMatch);  
  107.           
  108.         // 翻译Bean名到Bean对象本身  
  109.         if (handler instanceof String) {  
  110.             String handlerName = (String) handler;  
  111.             handler = getApplicationContext().getBean(handlerName);  
  112.         }  
  113.           
  114.         //调用占位符方法校验处理器  
  115.         validateHandler(handler, request);  
  116.           
  117.         //从URL中提取去除URL Pattern前缀的剩余部分,例如,URL Pattern是/petstore/*,而查找路径是/petstore/insert,则结构是/insert  
  118.         String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);  
  119.           
  120.         //得到模板变量,并且添加新的处理器拦截器到处到HTTP请求中,这些模板变量在控制器的实现中会被用到  
  121.         //例如, URL Pattern是/petstore/insert/{id}, 查找路径是insert/1,则解析出一个模板变量id=1,并且到处到HTTP请求对象里  
  122.         Map<String, String> uriTemplateVariables =  
  123.                 getPathMatcher().extractUriTemplateVariables(bestPatternMatch, urlPath);  
  124.           
  125.         //创建处理器执行链对象  
  126.         return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);  
  127.     }  
  128.     // No handler found...  
  129.     return null;  
  130. }  
  131. protected Object buildPathExposingHandler(Object rawHandler,  
  132.         String bestMatchingPattern,  
  133.         String pathWithinMapping,  
  134.         Map<String, String> uriTemplateVariables) {  
  135.     //创建处理器执行器链对象  
  136.     HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);  
  137.       
  138.     //添加路径到处理器拦截器,类定义如下  
  139.     chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));  
  140.       
  141.     //添加模板变量处理器拦截器,类定义如下  
  142.     if (!CollectionUtils.isEmpty(uriTemplateVariables)) {  
  143.         chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));  
  144.     }  
  145.     return chain;  
  146. }  
  147. //导出最佳匹配的URL Pattern和查找路径中去除URL Pattern所匹配的那部分  
  148. private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {  
  149.     private final String bestMatchingPattern;  
  150.     private final String pathWithinMapping;  
  151.     private PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {  
  152.         this.bestMatchingPattern = bestMatchingPattern;  
  153.         this.pathWithinMapping = pathWithinMapping;  
  154.     }  
  155.     @Override  
  156.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {  
  157.         //导出是在处理器拦截器的前置拦截器中实现的  
  158.         exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);  
  159.         return true;  
  160.     }  
  161.       
  162. }  
  163. protected void exposePathWithinMapping(String bestMatchingPattern, String pathWithinMapping, HttpServletRequest request) {  
  164.     //导出到请求属性中,在控制器中这些路径可以用来解析缺省的视图名  
  165.     request.setAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatchingPattern);  
  166.     request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);  
  167. }  
  168. //导出模板变量名值对  
  169. private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {  
  170.     private final Map<String, String> uriTemplateVariables;  
  171.     private UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {  
  172.         this.uriTemplateVariables = uriTemplateVariables;  
  173.     }  
  174.     @Override  
  175.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {  
  176.         //导出是在处理器拦截器的前置拦截器中实现的  
  177.         exposeUriTemplateVariables(this.uriTemplateVariables, request);  
  178.         return true;  
  179.     }  
  180. }  
  181. protected void exposeUriTemplateVariables(Map<String, String> uriTemplateVariables, HttpServletRequest request) {  
  182.     //导出到请求属性中,在控制器中这些参数可能成为业务逻辑的输入  
  183.     request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);  
  184. }  

 

 

我们看到,抽象 URL处理器映射是在 Web应用程序环境初始化的时候初始化了拦截器,并且提供了通过 URL对拦截器过滤的功能。同时提供方法注册处理器。现在我们将分析,一个子类应该如何注册处理器。在类的体系结构中的下一个类是抽象探测 URL处理器映射,它探测 Web应用程序环境中的所有 Bean,通过某种规则对Bean名字进行过滤来决定是否注册这个 Bean作为一个处理器。如下程序所示,

 

 

[java]  view plain copy
  1. //改写应用程序初始化方法,获得注册处理器的机会  
  2. @Override  
  3. public void initApplicationContext() throws ApplicationContextException {  
  4.     //保持原来的初始化实现  
  5.     super.initApplicationContext();  
  6.       
  7.     //从Web应用程序环境中探测处理器  
  8.     detectHandlers();  
  9. }  
  10. protected void detectHandlers() throws BeansException {  
  11.     if (logger.isDebugEnabled()) {  
  12.         logger.debug("Looking for URL mappings in application context: " + getApplicationContext());  
  13.     }  
  14.       
  15.     //找到所有的对象类的实现,其实是eb应用程序环境中所有的Bean,并且返回Bean名字  
  16.     String[] beanNames = (this.detectHandlersInAncestorContexts ?  
  17.             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :  
  18.             getApplicationContext().getBeanNamesForType(Object.class));  
  19.     // 对于每一个Bean的名字  
  20.     for (String beanName : beanNames) {  
  21.         //映射Bean的名字到一个或者多个URL  
  22.         String[] urls = determineUrlsForHandler(beanName);  
  23.         if (!ObjectUtils.isEmpty(urls)) {  
  24.             //如果这个Bean的名字能映射到一个或者多个URL,则注册Bean作为一个处理器  
  25.             registerHandler(urls, beanName);  
  26.         }  
  27.         else {  
  28.             //否则打印日志  
  29.             if (logger.isDebugEnabled()) {  
  30.                 logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");  
  31.             }  
  32.         }  
  33.     }  
  34. }  
  35. //提供子类机会选择不同的策略映射名字到URL Pattern  
  36. protected abstract String[] determineUrlsForHandler(String beanName);  

 

 

在上面的实现中,处理器是在应用程序环境中探测得到的。所以,我们称为这个类作为抽象探测 URL处理器映射。但是,在抽象 URL处理器映射中,初始化的实现也自动的在应用程序环境中探测了处理器拦截器的实现,所以,我认为把探测处理器拦截器的实现加入当前这个类中更合理。

 

事实上,映射 Bean的名字到 URL Pattern的实现是非常简单的,子类 Bean名 URL处理器映射通过查看是否一个Bean名字或者别名以字符 /开头,如果是以字符 /开头,则认为是一个处理器。如下程序所示,

 

 

[java]  view plain copy
  1. protected String[] determineUrlsForHandler(String beanName) {  
  2.     List<String> urls = new ArrayList<String>();  
  3.       
  4.     //如果Bean名以/开头  
  5.     if (beanName.startsWith("/")) {  
  6.         //则此Bean是一个处理器,Bean名字是此Bean的一个匹配的URL Pattern   
  7.         urls.add(beanName);  
  8.     }  
  9.       
  10.     //取得Bean的所有别名  
  11.     String[] aliases = getApplicationContext().getAliases(beanName);  
  12.     for (int i = 0; i < aliases.length; i++) {  
  13.         //如果Bean的别名以/开头  
  14.         if (aliases[i].startsWith("/")) {  
  15.             //则此Bean是一个处理器,Bean的别名是此Bean的一个匹配的URL Pattern  
  16.             urls.add(aliases[i]);  
  17.         }  
  18.     }  
  19.       
  20.     //返回此Bean定义的所有URL Pattern  
  21.     return StringUtils.toStringArray(urls);  
  22. }  

 

 

流程分析到这里,派遣器 Servlet已经通过处理器映射得到了处理器执行链对象。处理器执行链对象包含着一个以Object为类型的处理器,和一套应用在处理器上的处理器拦截器。接下来派遣器 Servlet则轮询所有注册的处理器适配器(如图 4-15 第二步),查找是否有一个处理器适配器支持此处理器。 Bean名 URL处理器映射(BeanNameUrlHandlerMapping)通常用于映射简单的控制器对象,所以,返回的处理器执行链对象里面通常包含着控制器接口( Controller)的实现类。这个轮询结果将返回简单控制处理适配器(SimpleControllerHandlerAdapter),并且通过它将 HTTP请求传递给控制器进行处理(如果 4-15 第四步和第五步)。简单的控制处理适配器的实现非常简单,正如它的名字所示,它仅仅是个适配器,请看如下类图,

 

图表 4 ‑17

 

如下程序注释,

 

 

[java]  view plain copy
  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {  
  2.     public boolean supports(Object handler) {  
  3.         支持任何控制器接口的实现类  
  4.         return (handler instanceof Controller);  
  5.     }  
  6.     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  7.             throws Exception {  
  8.         适配HTTP请求,HTTP响应到处理器,这个处理器一定是控制器接口的实现,并且返回模型和视图对象,派遣器Servlet讲用模型和视图对象构造HTTP响应  
  9.         return ((Controller) handler).handleRequest(request, response);  
  10.     }  
  11.     public long getLastModified(HttpServletRequest request, Object handler) {  
  12.         if (handler instanceof LastModified) {  
  13.             如果一个控制器实现了最后修改接口,则适配最后修改请求到控制器  
  14.             return ((LastModified) handler).getLastModified(request);  
  15.         }  
  16.           
  17.         如果没有实现了最后修改接口,则返回不支持最后修改操作,每次都返回一个全新的HTTP GET请求,尽管这个资源自从上次请求没有改变过  
  18.         return -1L;  
  19.     }  
  20. }  

 

 

现在我们理解,一个 HTTP请求经过处理器适配器传递到简单控制器的实现,简单控制器将实现任何必要的业务逻辑,最后返回模型数据和逻辑视图给作为总控制器的派遣器 Servlet。简单控制器接口有许多抽象和具体的实现,每个抽象和具体的实现都能完成一个特定的功能,结构清晰合理,易于扩展,使客户程序能够根据业务逻辑的需要选择不同的类继承处理不同的 HTTP请求。下面我们以简单 Form控制器为例说明它是如何完成对 HTTP请求的处理并且返回模型数据和逻辑视图对象给派遣器 Servlet的。下图是简单 Form控制器的实现流程,

 

 

图表 4 ‑18

 

从上图我们可以看到,简单 Form控制器是通过 HTTP请求方法来判断执行初始化操作还是业务逻辑操作。如果请求是 HTTP GET方法,说明这次请求是这个模块的第一次加载,那么我们应该显示一个 Form给用户,以至于用户可以填写业务逻辑的输入数据。当用户填写了业务逻辑数据后,提交 Form给此模块,那么用户提交使用的一定是 HTTP POST请求,这个请求包含着此模块业务逻辑的输入数据,所以,简单 Form控制器会绑定这些输入数据到业务逻辑模型对象中,这里称为一个命令 (Command)对象 ,以下分析中的命令对象, Form的 Backing Bean和业务逻辑模型对象指同一个事物,不再做区分。然后,对命令对象进行校验。如果在绑定或者校验过程中出现任何错误,则导出错误对象到 HTTP请求的属性里,接下来在显示 Form视图的时候,同时显示错误,于是,用户得知哪些输入是不合法的。当用户提交了完整而且有效的输入数据后,简单 Form控制器在绑定和校验后,获得了此模块需要的输入数据后,使用服务层的服务进行业务逻辑的处理,处理后会返回处理结果数据,也就是模型数据,这些模型数据连同成功视图一起返回给作为总控制器的派遣器 Servlet.

 

为了让程序具有可重用性和可扩展性,上面的流程并不是通过一个类实现的。而是通过多个类的继承最终由简单Form控制器实现的。如下是这些类的实现类图,

 

 

图表 4 ‑19

 

我们在分析处理器映射的实现中得知上图中被标识为灰色的类和接口是用来对一个对象注入 Web应用程序环境和Servlet环境的,这里我们不再详述其实现。继承自 Web应用程序对象支持类的第一个类就是 Web内容产生器,这个类用于校验支持的 HTTP方法,也会产生 HTTP缓存头信息等。如下程序注释,

 

 

[java]  view plain copy
  1. //这个方法实现检查HTTP请求方法的合法性和产生缓存头信息,这个类是抽象类,这个方法是实体方法给子类使用的  
  2. //lastModified通常是根据控制器是不是实现了LastModified接口来决定的  
  3. protected final void checkAndPrepare(  
  4.         HttpServletRequest request, HttpServletResponse response, boolean lastModified)  
  5.     throws ServletException {  
  6.     // 代理另外一个方法并且传入更多的参数(配置的缓存时间)  
  7.     checkAndPrepare(request, response, this.cacheSeconds, lastModified);  
  8. }  
  9. protected final void checkAndPrepare(  
  10.         HttpServletRequest request, HttpServletResponse response, int cacheSeconds, boolean lastModified)  
  11.     throws ServletException {  
  12.     // 检查是不是支持当前的HTTP请求方法  
  13.     String method = request.getMethod();  
  14.     if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {  
  15.         // 如果不支持,则抛出异常,终止处理  
  16.         throw new HttpRequestMethodNotSupportedException(  
  17.                 method, StringUtils.toStringArray(this.supportedMethods));  
  18.     }  
  19.     // 如果Session不存在,则抛出特定的异常HttpSessionRequiredException,这个异常通常被捕获,捕获后创建Session,然后重试  
  20.     if (this.requireSession) {  
  21.         if (request.getSession(false) == null) {  
  22.             throw new HttpSessionRequiredException("Pre-existing session required but none found");  
  23.         }  
  24.     }  
  25.     // 添加缓存信息到响应头中  
  26.     // 如果控制器支持最后修改操作,则设置必须重新校验信息到响应头中  
  27.     applyCacheSeconds(response, cacheSeconds, lastModified);  
  28. }  
  29. protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {  
  30.     // 如果缓存的时间大于0  
  31.     if (seconds > 0) {  
  32.         // 设置缓存时间到响应头中  
  33.         cacheForSeconds(response, seconds, mustRevalidate);  
  34.     }  
  35.     else if (seconds == 0) {  
  36.         // 设置绝不缓存信息到响应头中  
  37.         preventCaching(response);  
  38.     }  
  39.       
  40.     // 如果缓存时间小于0, 服务器不决定是否缓存,由客户自己决定  
  41. }  
  42. protected final void cacheForSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate) {  
  43.     // 这里我们需要兼容HTTP 1.0和HTTP 1.1  
  44.     if (this.useExpiresHeader) {  
  45.         // 如果是 HTTP 1.0 头信息,设置缓存的时间  
  46.         response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L);  
  47.     }  
  48.     if (this.useCacheControlHeader) {  
  49.         // 如果是HTTP 1.1 头信息,设置缓存时间  
  50.         String headerValue = "max-age=" + seconds;  
  51.           
  52.         //如果支持最后修改操作,则设置标识重新校验  
  53.         if (mustRevalidate) {  
  54.             headerValue += ", must-revalidate";  
  55.         }  
  56.           
  57.         response.setHeader(HEADER_CACHE_CONTROL, headerValue);  
  58.     }  
  59. }  
  60. protected final void preventCaching(HttpServletResponse response) {  
  61.     //在HTTP响应头中,设置不使用缓存  
  62.     response.setHeader(HEADER_PRAGMA, "no-cache");  
  63.     if (this.useExpiresHeader) {  
  64.         // 如果是HTTP 1.0 头信息,则使用1代表不使用缓存  
  65.         response.setDateHeader(HEADER_EXPIRES, 1L);  
  66.     }  
  67.     if (this.useCacheControlHeader) {  
  68.         // 如果是HTTP 1.1 头信息: "no-cache" 是标准值, "no-store" 用在firefox浏览器里的 ,他们都是用來告诉浏览器不需要缓存网页的  
  69.         response.setHeader(HEADER_CACHE_CONTROL, "no-cache");  
  70.         if (this.useCacheControlNoStore) {  
  71.             response.addHeader(HEADER_CACHE_CONTROL, "no-store");  
  72.         }  
  73.     }  
  74. }  

 

 

类层次的下一个类抽象控制器类实现了控制器接口。但是,这个类除了对方法 handleRequest()进行同步没有做任何实现,并且适配到一个抽象方法 handleRequestInternal(),这个方法是由子类来实现的。如下程序所示,

 

 

[java]  view plain copy
  1. public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)  
  2.         throws Exception {  
  3.     // 调用Web内容产生器进行检查HTTP方法和产生缓存信息的响应头  
  4.     checkAndPrepare(request, response, this instanceof LastModified);  
  5.     // 如果Session内同步标识打开,则对handleRequestInternal()进行同步调用  
  6.     if (this.synchronizeOnSession) {  
  7.         HttpSession session = request.getSession(false);  
  8.         if (session != null) {  
  9.             // 如果Session存在则取得同步对象,缺省是Session对象自己  
  10.             Object mutex = WebUtils.getSessionMutex(session);  
  11.             synchronized (mutex) {  
  12.                 // 在Session内同步处理请求  
  13.                 return handleRequestInternal(request, response);  
  14.             }  
  15.         }  
  16.     }  
  17.       
  18.     // 如果不需要Session内同步,直接调用了抽象方法handleRequestInternal()  
  19.     return handleRequestInternal(request, response);  
  20. }  
  21. // 这个方法需要由子类实现,来处理不同的业务流程  
  22. protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)  
  23.     throws Exception;  

 

 

类实现体系结构中的下一个类基本命令控制器中引入了领域对象模型命令对象 (Command)的概念,它是一个通用的对象类型,用来存储输入的参数信息。并且引入了对这个领域对象模型进行初始化和校验的逻辑,如下程序注释,

 

 

[java]  view plain copy
  1. //继承自应用程序支持类的初始化方法,实现更多的初始化逻辑  
  2. protected void initApplicationContext() {  
  3.     //如果注册了校验器  
  4.     if (this.validators != null) {  
  5.         for (int i = 0; i < this.validators.length; i++) {  
  6.             //如果存在着不支持配置的命令类的校验器,则抛出异常,停止处理  
  7.             if (this.commandClass != null && !this.validators[i].supports(this.commandClass))  
  8.                 throw new IllegalArgumentException("Validator [" + this.validators[i] +  
  9.                         "] does not support command class [" +  
  10.                         this.commandClass.getName() + "]");  
  11.         }  
  12.     }  
  13. }  
  14. //提供了取得命令的方法给子类使用  
  15. protected Object getCommand(HttpServletRequest request) throws Exception {  
  16.     //默认实现是通过配置的类实例化一个新的命令对象  
  17.     return createCommand();  
  18. }  
  19. protected final Object createCommand() throws Exception {  
  20.     //如果没有配置命令类,则抛出异常推出  
  21.     if (this.commandClass == null) {  
  22.         throw new IllegalStateException("Cannot create command without commandClass being set - " +  
  23.                 "either set commandClass or (in a form controller) override formBackingObject");  
  24.     }  
  25.     if (logger.isDebugEnabled()) {  
  26.         logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");  
  27.     }  
  28.       
  29.     //通过配置的类实例化一个命令对象  
  30.     return BeanUtils.instantiateClass(this.commandClass);  
  31. }  
  32. protected final boolean checkCommand(Object command) {  
  33.     //查看命令是否和配置的命令类匹配  
  34.     return (this.commandClass == null || this.commandClass.isInstance(command));  
  35. }  
  36. //提供方法进行绑定命令和校验命令给子类使用  
  37. protected final ServletRequestDataBinder bindAndValidate(HttpServletRequest request, Object command)  
  38. throws Exception {  
  39.     //通过当前HTTP请求和命令对象创建绑定对象  
  40.     ServletRequestDataBinder binder = createBinder(request, command);  
  41.       
  42.     //把绑定结构放入到版定异常中,erros和binder共享一个绑定结构的引用,校验中会把错误存贮在这个绑定结果上  
  43.     BindException errors = new BindException(binder.getBindingResult());  
  44.       
  45.     //查看是否配置了跳过绑定  
  46.     if (!suppressBinding(request)) {  
  47.         //如果没有跳过绑定  
  48.           
  49.         //绑定请求数据到命令数据里  
  50.         binder.bind(request);  
  51.           
  52.         //绑定后,激发绑定后事件  
  53.         onBind(request, command, errors);  
  54.           
  55.         //查看校验设置  
  56.         if (this.validators != null && isValidateOnBinding() && !suppressValidation(request, command, errors)) {  
  57.             //如果绑定时校验开启和跳过校验关闭,则使用校验器进行校验命令  
  58.             for (int i = 0; i < this.validators.length; i++) {  
  59.                 //在校验器里,如果有错误,就会存入errors对象里面,而errors是从绑定的绑定结果构造的,传递的是引用,所以共享一个结果对象,这个对象最后会被逐层返回  
  60.                 ValidationUtils.invokeValidator(this.validators[i], command, errors);  
  61.             }  
  62.         }  
  63.           
  64.         //绑定和校验后,激发绑定和校验后时间  
  65.         onBindAndValidate(request, command, errors);  
  66.     }  
  67.       
  68.     //如果设置了跳过绑定,则直接将绑定对象返回  
  69.     return binder;  
  70. }  
  71. protected boolean suppressBinding(HttpServletRequest request) {  
  72.     //默认情况下是进行绑定操作的  
  73.     return false;  
  74. }  
  75. protected void onBind(HttpServletRequest request, Object command, BindException errors) throws Exception {  
  76.     //响应绑定操作执行后的事件方法,子类可以改写实现特殊的绑定操作  
  77.     onBind(request, command);  
  78. }  
  79. protected void onBind(HttpServletRequest request, Object command) throws Exception {  
  80.     //占位符方法,同上方法同能  
  81. }  
  82. protected void onBindAndValidate(HttpServletRequest request, Object command, BindException errors)  
  83. throws Exception {  
  84.     //响应绑定和校验操作执行后的事件方法,子类可以改写实现特殊的校验操作  
  85. }  
  86. protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command)  
  87. throws Exception {  
  88.     //实例化一个Servlet请求数据绑定  
  89.     ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName());  
  90.       
  91.     //对绑定进行简单的初始化,例如,绑定使用的消息代码解析器,绑定错误处理器,客户化编辑器等  
  92.     prepareBinder(binder);  
  93.       
  94.     //提供机会通过Web绑定初始化器初始化绑定对象  
  95.     initBinder(request, binder);  
  96.       
  97.     //返回绑定对象  
  98.     return binder;  
  99. }  
  100. protected final void prepareBinder(ServletRequestDataBinder binder) {  
  101.     //设置是否使用直接字段存取  
  102.     if (useDirectFieldAccess()) {  
  103.         binder.initDirectFieldAccess();  
  104.     }  
  105.       
  106.     //设置消息代码解析器  
  107.     if (this.messageCodesResolver != null) {  
  108.         binder.setMessageCodesResolver(this.messageCodesResolver);  
  109.     }  
  110.       
  111.     //设置绑定错误处理器  
  112.     if (this.bindingErrorProcessor != null) {  
  113.         binder.setBindingErrorProcessor(this.bindingErrorProcessor);  
  114.     }  
  115.       
  116.     //这是客户化  
  117.     if (this.propertyEditorRegistrars != null) {  
  118.         for (int i = 0; i < this.propertyEditorRegistrars.length; i++) {  
  119.             this.propertyEditorRegistrars[i].registerCustomEditors(binder);  
  120.         }  
  121.     }  
  122. }  
  123. protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {  
  124.     if (this.webBindingInitializer != null) {  
  125.         //使用Web绑定初始化器对绑定进行初始化  
  126.         this.webBindingInitializer.initBinder(binder, new ServletWebRequest(request));  
  127.     }  
  128. }  

 

 

由此可见,基本命令控制器的实现中提供了实用方法创建命令对象,绑定命令对象和校验命令对象。那么,在下一个类层次中,抽象 Form控制器则使用这些方法进行创建命令对象,校验命令对象,进而实现整体的显示Form,处理 Form和显示成功试图的流程。如下程序注释,

 

 

[java]  view plain copy
  1. protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)  
  2.         throws Exception {  
  3.     // 判断是提交Form还是显示Form  
  4.     if (isFormSubmission(request)) {  
  5.         // 如果是提交Form(HTTP POST)  
  6.         try {  
  7.             // 取得命令对象,新创建的命令对象或者从Session中提取的命令对象,取决与配置  
  8.             Object command = getCommand(request);  
  9.             //   
  10.             使用父类提供的实用方法进行绑定和校验  
  11.             ServletRequestDataBinder binder = bindAndValidate(request, command);  
  12.               
  13.             // 构造绑定错误对象,这个对象是简历在绑定结构智商的  
  14.             BindException errors = new BindException(binder.getBindingResult());  
  15.               
  16.             // 处理提交Form逻辑  
  17.             return processFormSubmission(request, response, command, errors);  
  18.         }  
  19.         catch (HttpSessionRequiredException ex) {  
  20.             // 如果没有form-bean(命令对象)存在在Session里  
  21.             if (logger.isDebugEnabled()) {  
  22.                 logger.debug("Invalid submit detected: " + ex.getMessage());  
  23.             }  
  24.               
  25.             // 如果需要Session存在或者使用了Session From但是没有Session Form存在,则初始化Session Form,然后重试  
  26.             return handleInvalidSubmit(request, response);  
  27.         }  
  28.     }  
  29.     else {  
  30.         如果是显示Form(HTTP GET)  
  31.         // 则显示Form输入视图  
  32.         return showNewForm(request, response);  
  33.     }  
  34. }  
  35. protected boolean isFormSubmission(HttpServletRequest request) {  
  36.     // 只有HTTP POST才会使用提交Form的处理逻辑  
  37.     return "POST".equals(request.getMethod());  
  38. }  
  39. protected final Object getCommand(HttpServletRequest request) throws Exception {  
  40.     // 如果不是session-form 模式, 穿件一个全新的form-backing命令对象.  
  41.     if (!isSessionForm()) {  
  42.         return formBackingObject(request);  
  43.     }  
  44.     // 如果是Session-form 模式  
  45.     HttpSession session = request.getSession(false);  
  46.     if (session == null) {  
  47.         // 如果Session不存在,则抛出特殊的异常HttpSessionRequiredException  
  48.         throw new HttpSessionRequiredException("Must have session when trying to bind (in session-form mode)");  
  49.     }  
  50.     String formAttrName = getFormSessionAttributeName(request);  
  51.     // 取得用来保存在Session里的Session Form的关键字  
  52.     Object sessionFormObject = session.getAttribute(formAttrName);  
  53.     if (sessionFormObject == null) {  
  54.         //如果关键字不存在,则抛出特殊的异常HttpSessionRequiredException  
  55.         throw new HttpSessionRequiredException("Form object not found in session (in session-form mode)");  
  56.     }  
  57.       
  58.     // 抛出特殊的异常HttpSessionRequiredException,会被抓住,然后,创建Session和命令本身,然后重试  
  59.       
  60.     if (logger.isDebugEnabled()) {  
  61.         logger.debug("Removing form session attribute [" + formAttrName + "]");  
  62.     }  
  63.       
  64.     // 流程处理后,将命令对象从Session中移除,如果再次需要命令对象,则需要重建命令对象,然后绑定,校验等等  
  65.     session.removeAttribute(formAttrName);  
  66.     // 调用占位符方法取得当前的命令对象  
  67.     return currentFormObject(request, sessionFormObject);  
  68. }  
  69. protected Object formBackingObject(HttpServletRequest request) throws Exception {  
  70.     //使用父类的使用方法创建一个全新的form-backing命令对象  
  71.     return createCommand();  
  72. }  
  73. protected Object currentFormObject(HttpServletRequest request, Object sessionFormObject) throws Exception {  
  74.     // 返回传入的命令对象本身,也可能对命令对象做客户化的改变  
  75.     return sessionFormObject;  
  76. }  
  77. //抽象方法,子类用来实现对HTTP请求的处理逻辑  
  78. protected abstract ModelAndView processFormSubmission(  
  79.         HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)  
  80.         throws Exception;  
  81. //如果使用Session Form模式,但是没有Session或者Session里不存在命令对象  
  82. protected ModelAndView handleInvalidSubmit(HttpServletRequest request, HttpServletResponse response)  
  83. throws Exception {  
  84.     // 创建一个命令对象  
  85.     Object command = formBackingObject(request);  
  86.       
  87.     // 校验和绑定  
  88.     ServletRequestDataBinder binder = bindAndValidate(request, command);  
  89.     BindException errors = new BindException(binder.getBindingResult());  
  90.       
  91.     // 进行对HTTP提交请求的处理,这个方法是抽象方法,子类需要实现响应的对HTTP请求的处理逻辑  
  92.     return processFormSubmission(request, response, command, errors);  
  93. }  
  94. protected final ModelAndView showNewForm(HttpServletRequest request, HttpServletResponse response)  
  95. throws Exception {  
  96.     logger.debug("Displaying new form");  
  97.       
  98.     // 显示一个Form视图  
  99.     return showForm(request, response, getErrorsForNewForm(request));  
  100. }  
  101. protected final BindException getErrorsForNewForm(HttpServletRequest request) throws Exception {  
  102.     // 穿件命令对象(form-back对象)  
  103.     Object command = formBackingObject(request);  
  104.       
  105.     // 这个命令对象不能为空  
  106.     if (command == null) {  
  107.         throw new ServletException("Form object returned by formBackingObject() must not be null");  
  108.     }  
  109.       
  110.     // 简单命令对象和配置的命令类是否兼容  
  111.     if (!checkCommand(command)) {  
  112.         throw new ServletException("Form object returned by formBackingObject() must match commandClass");  
  113.     }  
  114.     //创建绑定对象,但是不需要真正的绑定和校验  
  115.     ServletRequestDataBinder binder = createBinder(request, command);  
  116.     BindException errors = new BindException(binder.getBindingResult());  
  117.       
  118.     // 缺省情况下对于显示Form视图,不绑定命令对象  
  119.     if (isBindOnNewForm()) {  
  120.         // 手工配置为显示Form视图时,进行绑定  
  121.         logger.debug("Binding to new form");  
  122.         binder.bind(request);  
  123.           
  124.         // 传递绑定时间  
  125.         onBindOnNewForm(request, command, errors);  
  126.     }  
  127.     // 返回绑定结果错误对象,这里面可能不包含错误  
  128.     return errors;  
  129. }  
  130. //子类实现用来决定如何显示Form视图  
  131. protected abstract ModelAndView showForm(  
  132.         HttpServletRequest request, HttpServletResponse response, BindException errors)  
  133.         throws Exception;  

 

 

如此可见,抽象 Form控制器实现了处理一个 Web Form的主要流程。也就是说,在 Form初始化的时候,则显示Form视图,而在 Form提交的时候,则处理 Form提交,最后显示成功视图。因此,定义了两个抽象的方法,showForm()和 processFormSubmission()。也正如我们所想,子类通过实现这两个方法来处理不同的流程。在简单 Form控制器的实现中, showForm()简单的显示了配置的 Form视图。而 processFormSubmission()的实现则判断是否有绑定和校验错误,如果有错误,则转发请求到 Form视图,在 Form视图中显示错误并且提示用户重新输入。如果用户输入了正确有效的数据并且提交 Form,简单 Form控制器则使用服务层的服务处理逻辑,并且连同包含处理结果的模型数据和成功视图返回给作为主控制器的派遣器 Servlet。如下程序注释,

 

 

[java]  view plain copy
  1. //实现父类的抽象方法,最终处理一个对Form初始化HTTP请求  
  2. @Override  
  3. protected ModelAndView showForm(  
  4.         HttpServletRequest request, HttpServletResponse response, BindException errors)  
  5.         throws Exception {  
  6.     return showForm(request, response, errors, null);  
  7. }  
  8. protected ModelAndView showForm(  
  9.         HttpServletRequest request, HttpServletResponse response, BindException errors, Map controlModel)  
  10.         throws Exception {  
  11.     //显示配置的Form视图  
  12.     return showForm(request, errors, getFormView(), controlModel);  
  13. }  
  14. @Override  
  15. protected ModelAndView processFormSubmission(  
  16.         HttpServletRequest request, HttpServletResponse response, Object command, BindException errors)  
  17.         throws Exception {  
  18.       
  19.     if (errors.hasErrors()) {  
  20.         if (logger.isDebugEnabled()) {  
  21.             logger.debug("Data binding errors: " + errors.getErrorCount());  
  22.         }  
  23.         //如果绑定和校验返回错误,则转发到Form视图,并且显示错误,提示用户重新输入  
  24.         return showForm(request, response, errors);  
  25.     }  
  26.     else if (isFormChangeRequest(request, command)) {  
  27.         logger.debug("Detected form change request -> routing request to onFormChange");  
  28.         //如果是Form改变请求,例如一个相关对话框数据改变等  
  29.         onFormChange(request, response, command, errors);  
  30.           
  31.         //转发到Form视图,继续用户输入  
  32.         return showForm(request, response, errors);  
  33.     }  
  34.     else {  
  35.         logger.debug("No errors -> processing submit");  
  36.           
  37.         //处理提交Form逻辑并且返回模型和视图对象  
  38.         return onSubmit(request, response, command, errors);  
  39.     }  
  40. }  
  41. protected ModelAndView onSubmit(Object command, BindException errors) throws Exception {  
  42.     //执行业务逻辑处理,返回模型和视图对象  
  43.     ModelAndView mv = onSubmit(command);  
  44.     if (mv != null) {  
  45.         // 如果返回了模型和视图对象,则直接返回它给派遣器Servlet,缺省情况下并不返回模型和视图对象  
  46.         return mv;  
  47.     }  
  48.     else {  
  49.         // 缺省情况下返回配置的成功视图  
  50.         if (getSuccessView() == null) {  
  51.             throw new ServletException("successView isn't set");  
  52.         }  
  53.         return new ModelAndView(getSuccessView(), errors.getModel());  
  54.     }  
  55. }  
  56. protected ModelAndView onSubmit(Object command) throws Exception {  
  57.     //业务逻辑处理的缺省实现,调用一个占位符方法  
  58.     //子类可以改写此方法的实现,调用服务层处理逻辑,返回模型和视图对象  
  59.     doSubmitAction(command);  
  60.     return null;  
  61. }  
  62. protected void doSubmitAction(Object command) throws Exception {  
  63.     //如果子类不需要返回特殊的视图,那么仅仅需要改写此方法  
  64. }  

 

 

简单 Form控制器是这个实现体系结构中的最后一个类,在使用它之前,需要为它配置 Form视图和成功视图,它就可以开始工作了。但是,一个真正的控制器类应该改写它的 doActionSubmit()方法,从而实现需要的业务逻辑调用。

 

最总我们可以看见,简单 Form 控制器实现并不是单一的类实现,在实现上有很多的层次,每个层次完成一个相对独立的功能,下一层紧紧的依赖于上一层。当你选择实现一个控制器的时候,可以根据需求选择实现哪个层次的抽象类控制器,甚至控制器接口本身。

分享到:
评论

相关推荐

    深入剖析Spring Web源码

    ### 深入剖析Spring Web源码 #### 一、Spring Web MVC介绍 **1.1 MVC体系结构** MVC(Model-View-Controller)是一种软件架构设计模式,被广泛应用于构建用户界面丰富的应用程序中。它将应用程序逻辑分为三个核心...

    spring-webmvc-5.0.9 jar包、源码和javadoc

    spring-webmvc-5.0.9.RELEASE-sources.jar则包含了源码,可以深入研究Spring Web MVC的实现细节,对于学习和调试都非常有帮助。 九、依赖管理 在实际项目中,Spring Web MVC往往与其他Spring模块如Core、AOP、Data...

    深入剖析Spring_Web源码.pdf

    ### 深入剖析Spring Web源码 #### 1. 前言 Spring作为一款卓越的轻量级企业级应用开发框架,在Java开发领域占据了举足轻重的地位。Spring框架以其灵活的依赖注入(Dependency Injection,DI)机制、强大的面向切面...

    spring源码spring-framework-4.3.2.RELEASE

    通过对Spring 4.3.2.RELEASE源码的深入研究,我们可以了解其设计理念,学习到如何优雅地管理依赖、实现面向切面编程,以及如何利用Spring构建高效、健壮的Web应用。同时,源码阅读也能帮助我们理解Spring如何与其他...

    官方原版源码spring-framework-5.0.14.RELEASE.zip

    3. **Web MVC**:Spring的Web MVC模块提供了一种灵活的处理HTTP请求的方法,包括控制器、模型-视图-适配器(MVC)模式以及模型绑定。在源码中,`DispatcherServlet`是整个Web MVC的入口点,它调度请求到相应的处理器...

    spring源码

    学习Spring源码有助于深入理解其内部工作原理,例如bean的生命周期管理、AOP的实现、以及MVC的请求处理流程。这将有助于开发者更高效地利用Spring框架,编写出高质量、高性能的Java应用。通过分析源码,开发者还可以...

    官方原版源码spring-framework-5.1.11.RELEASE.zip

    `org.springframework.web.servlet`包是Spring MVC的核心,`DispatcherServlet`作为核心控制器,负责请求的分发,`ModelAndView`处理视图模型,`HandlerMapping`和`HandlerAdapter`则负责映射请求到对应的处理器。...

    spring-framework-4.3.0源代码

    深入研究SpringMVC源代码,我们可以看到这些概念如何在底层实现,例如AOP(面向切面编程)如何用于拦截器的执行,以及IoC(控制反转)容器如何管理bean。此外,源代码还揭示了SpringMVC如何与其他Spring模块(如...

    spring-oxm源码

    《深入剖析Spring-oxm源码》 Spring-oxm是Spring框架的一部分,全称为Object/XML Mapping,它为Java对象与XML之间的映射提供了一种统一的抽象层。这个库使得开发者可以方便地在XML数据和Java对象之间进行转换,大大...

    spring-code-based

    Spring MVC 是 Spring 框架的重要组成部分,它为构建基于 Java 的 Web 应用程序提供了一种模型-视图-控制器(MVC)架构。本篇将深入探讨 Spring MVC 的核心概念、工作原理以及源码分析,旨在帮助开发者更高效地理解...

    spring-webmvc.rar

    通过阅读"spring-webmvc.rar"中的源代码,开发者可以了解到Spring Web MVC如何实现上述功能,以及如何进行定制和扩展,这对于提升Spring开发能力大有裨益。在Eclipse中导入源码后,可以结合断点调试,更深入地理解其...

    Spring MVC 源码

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建模型-视图-控制器(MVC)架构的应用程序提供了强大的支持。Spring MVC 的源码解析有助于开发者深入理解其内部工作原理,从而更好地利用该框架进行开发。下面...

    看透springMvc源代码分析与实践 源码

    在Spring MVC中,一个请求会经过一系列的流程,包括前端控制器(DispatcherServlet)、处理器映射器(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)等关键组件。以下是这些组件的...

    Spring宝典源码4

    Spring MVC的工作流程包括DispatcherServlet的初始化、请求映射、处理器适配器调用、模型数据绑定、视图渲染等步骤。源码分析可以帮助我们更好地理解这些组件如何协同工作,从而优化我们的Web应用开发。 此外,...

    springweb代码

    在这个"springweb代码"压缩包中,很可能是包含了一些Spring Web项目的源码或者配置文件。下面,我们将深入探讨Spring Web的相关知识点。 1. **Spring MVC架构**: - **Model**:模型层,通常由JavaBean组成,存储...

    Spring-MVC-step-by-step2.rar

    3. HandlerMapping:处理器映射器,根据请求URL找到对应的Controller方法。 4. HandlerAdapter:处理器适配器,执行Controller方法。 5. ViewResolver:视图解析器,根据ModelAndView中的视图名生成实际的视图。 6. ...

    京东T5级大牛带你解读Spring核心源码——1小时手写SpringMVC~

    ### Spring核心源码解读与手动实现SpringMVC #### 一、Spring框架简介 Spring框架是由Rod Johnson在2004年发起的一个开源项目,它是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器。Spring框架的核心特性包括:...

    web项目整合spring

    - **创建servlet-context.xml**:这个文件定义了Spring MVC的配置,包括视图解析器、处理器映射器、处理器适配器等。 - **配置Spring beans**:在Spring配置文件中定义你需要的bean,例如控制器(Controller)、...

    spring-webmvc-3.0.2.RELEASE.jar.zip

    3. HandlerAdapter:适配器模式,允许使用不同的控制器实现,使得Controller可以以统一的方式被调用。 4. ModelAndView:包含了处理结果和视图信息,用于传递模型数据到视图进行渲染。 5. ViewResolver:视图解析器...

    spring-webmvc.src.zip

    4. **org.springframework.web.servlet.mvc**: MVC模式的具体实现,包括抽象控制器类和具体的控制器实现,如AbstractController和Controller接口,以及基于注解的控制器如RequestMappingHandlerMapping和...

Global site tag (gtag.js) - Google Analytics