Spring源码分析3----SpringMVC的设计与实现和视图的呈现

来源:互联网 发布:淘宝api转换淘口令 编辑:程序博客网 时间:2024/05/18 13:11

Spring源码分析3----SpringMVC的设计与实现和视图的呈现



目录(?)[+]

本文参考<spring技术内幕>,spring版本为4.3.2

1.Spring MVC的应用模式

    Spring MVC是一个MVC模式的实现,在配置文件中DispatcherServlet实现的是SUn的j2EE核心模式中的前端控制器模式(Front Controller),作为一个前端控制器,所有的Web请求都需要通过它来处理,进行转发、匹配、数据处理后,并转由页面进行展现,因此这个DispatcherServlet可以看成是Spring MVC实现中最核心的部分。

    DispatcherServlet的工作大致可以分成两个部分,一个是初始化部分,接口在Servlet的init方法,由initServletBean()启动,最终调用DispatcherServlet的initStrategies方法; 另一个是对HTTP请求进行响应,作为Servlet,Web容器会调用Servlet的doGet()和doPost()方法,最终调用DispatcherServlet的doService()方法方法。


2.DispatcherServlet启动和初始化

2.1.启动
    作为Servlet,DispatcherServlet的启动与Servlet的启动过程是相联系的。在Servlet的启动过程中,Servlet的init方法会被调用,以进行初始化。DispatcherServlet的基类HttpServletBean中的这个初始化init()过程如下。
    在初始化开始时,需要读取配置在ServletContext中的Bean属性参数,这些属性参数设置在web.xml的web容器初始化参数中。使用编程式(通过代码而不是注解)的方式来设置这些Bean属性,在这里可以看到对PropertyValues和BeanWrapper的使用。对于这些和依赖注入相关的类的使用,在分析IoC容器初始化时尤其是在依赖注入实现分析时,有过接触。只是这里的依赖注入是与Web容器初始化想着,初始化过程由HttpServletBean来完成。
    接着会执行DispatcherServlet持有IoC容器的初始化过程,在这个初始化过程中,一个新的上下文被建立起来,这个DispatcherServlet持有的上下文被设置为根上下文 的子上下文。可以认为,根上下文是和Web应用相对应的一个上下文,而DispatcherServlet持有的上下文是和Servlet对应的一个上下文。在一个Web应用中,往往可以容纳多个Servlet存在;与些相对应,对于应用在Web容器中的上下文体系,一个根上下文可以作为许多Servlet上下文的双亲上下文。了解这一点,对在Web环境中IoC容器中的Bean设置和检索会有更多的了解,因为了解IoC工作原理知道,在向IoC容器getBean时,IoC容器会首先向其双亲上下文去getBean,也就是说,在根上下文中定义的Bean是可以被各个Servlet持有的上下文得到和共享的。DispatcherServlet持有的上下文被建立起来以后,也需要和其他IoC容器一样完成初始化,这个初始化也是通过refresh方法来完成的。最后,DispatcherServlet给这个自己持有的上下文命名,并把它设置到Web容器的上下文中,这个名称和Web.xml中设置的DispatcherServlet的Servlet名称有关,从而保证了这个上下文在Web环境上下文体系中的唯一性。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // public abstract class HttpServletBean extends HttpServlet  
  2. //      implements EnvironmentCapable, EnvironmentAware   
  3.   
  4.     @Override  
  5.     public final void init() throws ServletException {  
  6.         if (logger.isDebugEnabled()) {  
  7.             logger.debug("Initializing servlet '" + getServletName() + "'");  
  8.         }  
  9.   
  10.         // 获取Servlet的初始化参数,对Bean属性进行配置  
  11.         try {  
  12.             PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);  
  13.             BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
  14.             ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());  
  15.             bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, getEnvironment()));  
  16.             initBeanWrapper(bw);  
  17.             bw.setPropertyValues(pvs, true);  
  18.         }  
  19.         catch (BeansException ex) {  
  20.             logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);  
  21.             throw ex;  
  22.         }  
  23.   
  24.         // 调用子类的方法进行具体的初始化,模板方法  
  25.         initServletBean();  // ---------------->  
  26.   
  27.         if (logger.isDebugEnabled()) {  
  28.             logger.debug("Servlet '" + getServletName() + "' configured successfully");  
  29.         }  
  30.     }  
  31.   
  32.     protected void initServletBean() throws ServletException {  
  33.     }  
  34.   
  35. // ------------------------------------  
  36. // FrameworkServlet  
  37.   
  38.     @Override  
  39.     protected final void initServletBean() throws ServletException {  
  40.         getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");  
  41.         if (this.logger.isInfoEnabled()) {  
  42.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");  
  43.         }  
  44.         long startTime = System.currentTimeMillis();  
  45.   
  46.         // 这里初始化上下文  
  47.         try {  
  48.             this.webApplicationContext = initWebApplicationContext(); // ----------->  
  49.             initFrameworkServlet();  
  50.         }  
  51.         catch (ServletException ex) {  
  52.             this.logger.error("Context initialization failed", ex);  
  53.             throw ex;  
  54.         }  
  55.         catch (RuntimeException ex) {  
  56.             this.logger.error("Context initialization failed", ex);  
  57.             throw ex;  
  58.         }  
  59.   
  60.         if (this.logger.isInfoEnabled()) {  
  61.             long elapsedTime = System.currentTimeMillis() - startTime;  
  62.             this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +  
  63.                     elapsedTime + " ms");  
  64.         }  
  65.     }     
  66.   
  67.   
  68.     protected WebApplicationContext initWebApplicationContext() {  
  69.   
  70.         // 这里调用WebApplicationContextUtils静态类来得到根上下文,  
  71.         // 这个根上下文是保存在ServletContext中的,  
  72.         // 使用这个根上下文作为当前MVC上下文的双亲上下文   
  73.         WebApplicationContext rootContext =  
  74.                 WebApplicationContextUtils.getWebApplicationContext(getServletContext()); // ---->  
  75.         WebApplicationContext wac = null;  
  76.   
  77.         if (this.webApplicationContext != null) {  
  78.             wac = this.webApplicationContext;  
  79.             if (wac instanceof ConfigurableWebApplicationContext) {  
  80.                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;  
  81.                 if (!cwac.isActive()) {  
  82.                     if (cwac.getParent() == null) {  
  83.                         cwac.setParent(rootContext);  
  84.                     }  
  85.                     configureAndRefreshWebApplicationContext(cwac);  
  86.                 }  
  87.             }  
  88.         }  
  89.         if (wac == null) {  
  90.             wac = findWebApplicationContext();  
  91.         }  
  92.         if (wac == null) {  
  93.             // ------------------>  
  94.             wac = createWebApplicationContext(rootContext);  
  95.         }  
  96.   
  97.         if (!this.refreshEventReceived) {  
  98.             onRefresh(wac);   // -------->  
  99.         }  
  100.   
  101.         // 把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的  
  102.         if (this.publishContext) {  
  103.             String attrName = getServletContextAttributeName();  
  104.             getServletContext().setAttribute(attrName, wac);  
  105.             if (this.logger.isDebugEnabled()) {  
  106.                 this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +  
  107.                         "' as ServletContext attribute with name [" + attrName + "]");  
  108.             }  
  109.         }  
  110.   
  111.         return wac;  
  112.     }  
    在这里,这个MVC的上下文就建立起来了,具体取得根上下文的过程在WebApplicationContextUtils中实现,如下代码。
    这个根上下文是ContextLoader设置到ServletContext中去,使用的属性是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,同时对这个IoC容器的Bean配置文件,ContextLoader也进行了设置,默认的位置是在/WEB-INF/applicationContext.xml文件中。由于这个根上下文是DispatcherServlet建立的上下文双亲上下文,所以根上下文中管理的Bean也是可以被DispatcherServlet的上下文使用的。通过getBean向IoC容器获取Bean时,容器会先到它的双亲IoC容器中获取getBean,这些在分析ioC容器里有讲过。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // public abstract class WebApplicationContextUtils   
  2.   
  3.     public static WebApplicationContext getWebApplicationContext(ServletContext sc) {  
  4.         return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);  
  5.     }  
-------------------------------
    回到FrameworkServlet的实现中来看一下DispatcherServlet的上下文是怎样建立的。这个建立过程与前面建立根上下文的过程非常类似,如下代理。建立DispatcherServlet的上下文,需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象,并为它设置参数。根据默认的配置,这个上下文对象也是XmlWebApplicationContext对象,这个类型是在DEFAULT_CONTEXT_CLASS 参数中设置好并允许BeanUtils使用的。在实例化结束以后,需要为这个上下文对象设置好一些基本的配置,这些配置包括它的双亲上下文、Bean定义配置的文件位置等。完成这些配置以后,最后通过调用IoC容器的refresh方法来完成IoC容器的最终初始化,这和前面我们对IoC容器实现原理的分析 所看到的IoC容器初始化过程是一致的。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //FrameworkServlet 建立WebApplicationContext  
  2.   
  3.     public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;  
  4.   
  5.     private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;  
  6.   
  7.     public Class<?> getContextClass() {  // -->  
  8.         return this.contextClass;  
  9.     }  
  10.   
  11.     protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {  
  12.         return createWebApplicationContext((ApplicationContext) parent);  
  13.     }  
  14.   
  15.     protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {  
  16.   
  17.         Class<?> contextClass = getContextClass();  // -----|这里得到的是XmlWebApplicationContext  
  18.   
  19.         if (this.logger.isDebugEnabled()) {  
  20.             this.logger.debug("Servlet with name '" + getServletName() +  
  21.                     "' will try to create custom WebApplicationContext context of class '" +  
  22.                     contextClass.getName() + "'" + ", using parent context [" + parent + "]");  
  23.         }  
  24.         if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
  25.             throw new ApplicationContextException(  
  26.                     "Fatal initialization error in servlet with name '" + getServletName() +  
  27.                     "': custom WebApplicationContext class [" + contextClass.getName() +  
  28.                     "] is not of type ConfigurableWebApplicationContext");  
  29.         }  
  30.   
  31.         // DispatcherServlet中使用的IoC容器是XmlWebApplicationContext  
  32.         ConfigurableWebApplicationContext wac =  
  33.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
  34.   
  35.         wac.setEnvironment(getEnvironment());  
  36.         wac.setParent(parent);  
  37.         wac.setConfigLocation(getContextConfigLocation());  
  38.   
  39.         configureAndRefreshWebApplicationContext(wac); // ---->  
  40.   
  41.         return wac;  
  42.     }  
  43.   
  44.   
  45.     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {  
  46.         if (ObjectUtils.identityToString(wac).equals(wac.getId())) {  
  47.             if (this.contextId != null) {  
  48.                 wac.setId(this.contextId);  
  49.             }  
  50.             else {  
  51.                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
  52.                         ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());  
  53.             }  
  54.         }  
  55.   
  56.         wac.setServletContext(getServletContext());  
  57.         wac.setServletConfig(getServletConfig());  
  58.         wac.setNamespace(getNamespace());  
  59.         wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));  
  60.   
  61.         ConfigurableEnvironment env = wac.getEnvironment();  
  62.         if (env instanceof ConfigurableWebEnvironment) {  
  63.             ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());  
  64.         }  
  65.   
  66.         postProcessWebApplicationContext(wac);  
  67.         applyInitializers(wac);  
  68.         // 这里同样是调用refresh来调用容器的初始化过程的  
  69.         wac.refresh();  // ------->  
  70.     }  
    这个时候DispatcherServlet中的IoC容器已经建立起来了,这个IoC容器是根上下文的子容器。这样的设置,使得对具体的一个Bean定义查找过程来说,如果要查找一个由DispatcherServlet所在的IoC容器来管理的Bean,系统会首先到根上下文中去查找。如果查找不到,才会到DispatcherServlet所管理的IoC容器去进行查找,这是由IoC容器getBean的实现来决定的。关于这个机制,可以参考前面的getBean实现的分析。通过一系列在Web容器中执行的动作,在这个上下文体系建立和初始化完毕的基础上,Spring MVC 就可以发挥其作用了。下面来分析一下Spring MVC的具体实现。

2.2.初始化
    在前面分析DispatcherServlet的初始化过程中可以看到,DispatcherServlet持有一个以自己的Servlet名称命名的IoC容器。这个IoC容器是一个WebApplicationContext对象,这个IoC容器建立起来以后,意味着DispatcherServlet拥有自己的Bean定义空间,这为使用各个独立的XML文件来配置MVC中各个Bean创造了条件。由于在初始化结束以后,与Web容器相关的加载过程实际上已经完成了,Spring MVC的具体实现和普通的Spring应用程序的实现并没有太大的差别。在Spring MVC DispatcherServlet的初始化过程中,以对HandlerMapping的初始化调用作为触发点,了解Spring MVC模块初始化的方法调用关系。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //FrameworkServlet  
  2.   
  3.     protected void onRefresh(ApplicationContext context) {  
  4.         // For subclasses: do nothing by default.  
  5.     }  
  6.   
  7. //--------------------------------------  
  8. // public class DispatcherServlet extends FrameworkServlet {  
  9.   
  10.     @Override  
  11.     protected void onRefresh(ApplicationContext context) {  
  12.         initStrategies(context);  
  13.     }  
  14.   
  15.     protected void initStrategies(ApplicationContext context) {  
  16.         initMultipartResolver(context);  // 1.处理文件上传服务  
  17.         initLocaleResolver(context);  // 2.处理国际化  
  18.         initThemeResolver(context);  // 3.处理主题  
  19.         initHandlerMappings(context);  // 4.请求映射关系  
  20.         initHandlerAdapters(context);  // 5.根据Hander类型定义不同处理adapter  
  21.         initHandlerExceptionResolvers(context);  // 6.异常处理  
  22.         initRequestToViewNameTranslator(context);  // 7.将viewname转换成想要的格式,如jsp  
  23.         initViewResolvers(context);  // 8.view解析成页面  
  24.         initFlashMapManager(context);  // 9.  
  25.     }  
    对于具体的初始化过程,根据上面的方法名称,很容易理解。以HanderMapping为例来说明这个initHanderMappings过程。这里的Mapping关系的作用是,为HTTP请求找到相应 的Controller控制器,从而利用这些控制器Controller去完成设计好的数据处理工作。HandlerMappings完成对MVC中Controller的定义和配置,只不过在Web这个特定的应用环境中,这些控制器是与具体的HTTP请求相对应的。DispatcherServlet中HandlerMappings初始化过程的具体实现如下。在HandlerMapping初始化的过程中,把在Bean配置文件中配置好的handlerMapping从IoC容器中取得。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // 对HandlerMapping的初始化  
  2.   
  3.     private void initHandlerMappings(ApplicationContext context) {  
  4.         this.handlerMappings = null;  
  5.   
  6.         /* 这里导入所有的HandlerMapping Bean,这些Bean可以在当前的DispatcherServlet的IoC容器中, 
  7.           也可能在其双亲上下文中,这个detectAllHandlerMappings的默认值设为true, 
  8.           即默认地从所有的IoC容器中取*/  
  9.         if (this.detectAllHandlerMappings) {  
  10.             Map<String, HandlerMapping> matchingBeans =  
  11.                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.classtruefalse);  
  12.             if (!matchingBeans.isEmpty()) {  
  13.                 this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());  
  14.                 AnnotationAwareOrderComparator.sort(this.handlerMappings);  
  15.             }  
  16.         }  
  17.         else {  // 可以根据名称从当前的IoC容器中通过getBean获取handlerMapping  
  18.             try {  
  19.                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
  20.                 this.handlerMappings = Collections.singletonList(hm);  
  21.             }  
  22.             catch (NoSuchBeanDefinitionException ex) {  
  23.                 // Ignore, we'll add a default HandlerMapping later.  
  24.             }  
  25.         }  
  26.   
  27.         /* 如果没有找到handerMappings,那么需要为Servlet设定默认的handlerMappings, 
  28.           这些默认的值可以设置在DispatcherServlet.properties*/  
  29.         if (this.handlerMappings == null) {  
  30.             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);  
  31.             if (logger.isDebugEnabled()) {  
  32.                 logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");  
  33.             }  
  34.         }  
  35.     }  
    经过以上的读取过程,handlerMappings变量就已经获取了在BeanDefinition中配置好的映射关系。其他的初始化过程和handlerMappings比较类似,都是直接从IoC容器中读入配置,所以这里的MVC初始化过程是建立在IoC容器已经初始化完成的基础上的。至于上下文是如何获得的,可以参见前面对IoC容器在Web环境中加载的实现原理的分析。


3.MVC处理HTTP分发请求

3.1.HandlerMapping的配置和设计原理
    在初始化完成时,在上下文环境中已定义的所有HandlerMapping都已经被加载了,这些加载的handlerMappings被放在一个List中并排序,存储着HTTP请求对应的映射数据。这个List中的每一个元素都对应着一个具体handlerMapping的配置,一般每一个handlerMapping可以持有一系列从URL请求到Controller的映射,而Spring MVC 提供了一系列的HandlerMapping实现。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public interface HandlerMapping {  
  2.   
  3.     String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";  
  4.   
  5.     String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";  
  6.   
  7.     String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";  
  8.   
  9.     String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";  
  10.   
  11.     String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";  
  12.   
  13.     String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";  
  14.   
  15.     /* 调用这个方法实际上返回的是一个HandlerExecutionChain,这是典型的Commoand的模式的使用, 
  16.       这个HandlerExecutionChain不但持有handler本身,还包括了处理这个HTTP请求相关的拦截器。*/  
  17.     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  
  18.   
  19. }  
    这个HandlerExecutionChain的实现看起来比较简洁,它持有一个拦截器链和一个handler对象,这个handler对象实际上就是HTTP请求对应的Controller,在持有这个handler对象的同时,还在HandlerExecutionChain中设置了拦截器链,通过这个拦截器链中的拦截器,可以为handler对象提供功能的增强。要完成这些工作,需要对拦截器链和handler都进行配置,这些配置都是在这个类的初始化函数中完成的。
    为了维护这个拦截器和handler, HandlerExecutionChain还提供了一系列与拦截器链维护相关一些操作,比如可以为拦截器链增加拦截器的addInterceptor方法等。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // ----------------HandlerExecutionChain的实现-------------------  
  2.   
  3. public class HandlerExecutionChain {  
  4.   
  5.     private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);  
  6.   
  7.     private final Object handler;  
  8.     private HandlerInterceptor[] interceptors;  
  9.     private List<HandlerInterceptor> interceptorList;  
  10.   
  11.     private int interceptorIndex = -1;  
  12.   
  13.   
  14.     public HandlerExecutionChain(Object handler) {  
  15.         this(handler, (HandlerInterceptor[]) null);  
  16.     }  
  17.   
  18.     public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {  
  19.         if (handler instanceof HandlerExecutionChain) {  
  20.             HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;  
  21.             this.handler = originalChain.getHandler();  
  22.             this.interceptorList = new ArrayList<HandlerInterceptor>();  
  23.             CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);  
  24.             CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);  
  25.         }  
  26.         else {  
  27.             this.handler = handler;  
  28.             this.interceptors = interceptors;  
  29.         }  
  30.     }  
  31.   
  32.     public Object getHandler() {  
  33.         return this.handler;  
  34.     }  
  35.   
  36.     public void addInterceptor(HandlerInterceptor interceptor) {  
  37.         initInterceptorList().add(interceptor);  
  38.     }  
  39.   
  40.     public void addInterceptors(HandlerInterceptor... interceptors) {  
  41.         if (!ObjectUtils.isEmpty(interceptors)) {  
  42.             initInterceptorList().addAll(Arrays.asList(interceptors));  
  43.         }  
  44.     }  
  45.   
  46.     private List<HandlerInterceptor> initInterceptorList() {  
  47.         if (this.interceptorList == null) {  
  48.             this.interceptorList = new ArrayList<HandlerInterceptor>();  
  49.             if (this.interceptors != null) {  
  50.                 // An interceptor array specified through the constructor  
  51.                 this.interceptorList.addAll(Arrays.asList(this.interceptors));  
  52.             }  
  53.         }  
  54.         this.interceptors = null;  
  55.         return this.interceptorList;  
  56.     }  
  57.   
  58.     public HandlerInterceptor[] getInterceptors() {  
  59.         if (this.interceptors == null && this.interceptorList != null) {  
  60.             this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);  
  61.         }  
  62.         return this.interceptors;  
  63.     }  
  64.   
  65.     boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  66.         HandlerInterceptor[] interceptors = getInterceptors();  
  67.         if (!ObjectUtils.isEmpty(interceptors)) {  
  68.             for (int i = 0; i < interceptors.length; i++) {  
  69.                 HandlerInterceptor interceptor = interceptors[i];  
  70.                 if (!interceptor.preHandle(request, response, this.handler)) {  
  71.                     triggerAfterCompletion(request, response, null);  
  72.                     return false;  
  73.                 }  
  74.                 this.interceptorIndex = i;  
  75.             }  
  76.         }  
  77.         return true;  
  78.     }  
  79.   
  80.     void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {  
  81.         HandlerInterceptor[] interceptors = getInterceptors();  
  82.         if (!ObjectUtils.isEmpty(interceptors)) {  
  83.             for (int i = interceptors.length - 1; i >= 0; i--) {  
  84.                 HandlerInterceptor interceptor = interceptors[i];  
  85.                 interceptor.postHandle(request, response, this.handler, mv);  
  86.             }  
  87.         }  
  88.     }  
  89.   
  90.     void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)  
  91.             throws Exception {  
  92.   
  93.         HandlerInterceptor[] interceptors = getInterceptors();  
  94.         if (!ObjectUtils.isEmpty(interceptors)) {  
  95.             for (int i = this.interceptorIndex; i >= 0; i--) {  
  96.                 HandlerInterceptor interceptor = interceptors[i];  
  97.                 try {  
  98.                     interceptor.afterCompletion(request, response, this.handler, ex);  
  99.                 }  
  100.                 catch (Throwable ex2) {  
  101.                     logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);  
  102.                 }  
  103.             }  
  104.         }  
  105.     }  
  106.   
  107.     void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {  
  108.         HandlerInterceptor[] interceptors = getInterceptors();  
  109.         if (!ObjectUtils.isEmpty(interceptors)) {  
  110.             for (int i = interceptors.length - 1; i >= 0; i--) {  
  111.                 if (interceptors[i] instanceof AsyncHandlerInterceptor) {  
  112.                     try {  
  113.                         AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];  
  114.                         asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);  
  115.                     }  
  116.                     catch (Throwable ex) {  
  117.                         logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);  
  118.                     }  
  119.                 }  
  120.             }  
  121.         }  
  122.     }  
  123.   
  124.     @Override  
  125.     public String toString() {  
  126.         if (this.handler == null) {  
  127.             return "HandlerExecutionChain with no handler";  
  128.         }  
  129.         StringBuilder sb = new StringBuilder();  
  130.         sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");  
  131.         if (!CollectionUtils.isEmpty(this.interceptorList)) {  
  132.             sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");  
  133.             if (this.interceptorList.size() > 1) {  
  134.                 sb.append("s");  
  135.             }  
  136.         }  
  137.         return sb.toString();  
  138.     }  
  139.   
  140. }  
    HandlerExecutionChain中定义的Handler和Interceptor需要在定义HandlerMapping时配置好,例如对具体的SimpleURLHandlerMapping, 要做的就是根据URL映射的方式,注册Handler和Interceptor,从而维护一个反映这种映射关系的handlerMap。当需要匹配HTTP请求时,需要查询这个handlerMap中信息来得到对应的HandlerExecutionChain。这些信息是什么时候配置好的呢?这里有一个注册过程,这个注册过程在容器对Bean进行依赖注入时发生,它实际上是通过一个Bean的postProcessor来完成的。如果想了解这个过程,可以从依赖注入那里去看看,doCreateBean->initializeBean -> postProcessBeforeInitialization -> setApplicationContext -> initApplicationContext.
    以SimpleUrlHandlerMapping为例,需要注意的是,这里用到了对容器的回调,只有SimpleUrlHandlerMapping是AppicationContextAware的子类才能启动这个注册过程。这个注册过程完成的是反映URL和Controller之间的映射关系的handlerMap的建立。具体分析如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {  
  2.   
  3.     private final Map<String, Object> urlMap = new LinkedHashMap<String, Object>();  
  4.   
  5.     public void setMappings(Properties mappings) {  
  6.         CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);  
  7.     }  
  8.   
  9.   
  10.     public void setUrlMap(Map<String, ?> urlMap) {  
  11.         this.urlMap.putAll(urlMap);  
  12.     }  
  13.     public Map<String, ?> getUrlMap() {  
  14.         return this.urlMap;  
  15.     }  
  16.   
  17.     // 注册handler  
  18.     @Override  
  19.     public void initApplicationContext() throws BeansException {  
  20.         super.initApplicationContext();  
  21.         registerHandlers(this.urlMap);  
  22.     }  
  23.   
  24.     protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {  
  25.         if (urlMap.isEmpty()) {  
  26.             logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");  
  27.         }  
  28.         else {  
  29.             // 这里对Bean的配置进行解析,然后调用基类的registerHandler完成注册  
  30.             for (Map.Entry<String, Object> entry : urlMap.entrySet()) {  
  31.                 String url = entry.getKey();  
  32.                 Object handler = entry.getValue();  
  33.                 // Prepend with slash if not already present.  
  34.                 if (!url.startsWith("/")) {  
  35.                     url = "/" + url;  
  36.                 }  
  37.                 // Remove whitespace from handler bean name.  
  38.                 if (handler instanceof String) {  
  39.                     handler = ((String) handler).trim();  
  40.                 }  
  41.                 registerHandler(url, handler);  // -------->  
  42.             }  
  43.         }  
  44.     }  
  45.   
  46. }  
这个SimpleUrlHandlerMapping注册过程的完成,很大一部分需要它的基类来配合,这个基类就是AbstractUrlHandlerMapping.
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //----------------------------------------  
  2. // public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {  
  3.   
  4.     // 保存了URL请求和Controller的映射关系  
  5.     private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();  
  6.   
  7.     /* 在这个处理过程中,如果使用Bean的名字作为映射,那么直接从容器中获取这个HTTP映射对应的Bean, 
  8.       然后还要对不同的URL配置进行解析处理,比如在HTTP请求中配置成"/"和配置符"/*"的URL,以及正常的URL请求, 
  9.       完成这个解析处理后,会把URL和handler作为键值对应放到一个handlerMap中去*/  
  10.     protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {  
  11.         Assert.notNull(urlPath, "URL path must not be null");  
  12.         Assert.notNull(handler, "Handler object must not be null");  
  13.         Object resolvedHandler = handler;  
  14.   
  15.         // 如果直接用bean名称进行映射,那就直接从窗口中获取handler  
  16.         if (!this.lazyInitHandlers && handler instanceof String) {  
  17.             String handlerName = (String) handler;  
  18.             if (getApplicationContext().isSingleton(handlerName)) {  
  19.                 resolvedHandler = getApplicationContext().getBean(handlerName);  
  20.             }  
  21.         }  
  22.   
  23.         Object mappedHandler = this.handlerMap.get(urlPath);  
  24.         if (mappedHandler != null) {  
  25.             if (mappedHandler != resolvedHandler) {  
  26.                 throw new IllegalStateException(  
  27.                         "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +  
  28.                         "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");  
  29.             }  
  30.         }  
  31.         else { // 处理URL是"/"的映射,把这个"/"映射的controller设置到rootHandler中  
  32.             if (urlPath.equals("/")) {  
  33.                 if (logger.isInfoEnabled()) {  
  34.                     logger.info("Root mapping to " + getHandlerDescription(handler));  
  35.                 }  
  36.                 setRootHandler(resolvedHandler);  
  37.             }  
  38.             // 处理URL是"/*"的映射,把这个"/"映射的Controler设置到defaultHandler中  
  39.             else if (urlPath.equals("/*")) {  
  40.                 if (logger.isInfoEnabled()) {  
  41.                     logger.info("Default mapping to " + getHandlerDescription(handler));  
  42.                 }  
  43.                 setDefaultHandler(resolvedHandler);  
  44.             }  
  45.             // 处理正常的URL映射,设置handlerMap的key和value,分别对应于URL和映射的controller  
  46.             else {  
  47.                 this.handlerMap.put(urlPath, resolvedHandler);  
  48.                 if (logger.isInfoEnabled()) {  
  49.                     logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));  
  50.                 }  
  51.             }  
  52.         }  
  53.     }  
    这个配置好URL请求和handler映射数据的handlerMap,为Spring MVC响应HTTP请求准备好了基本的映射数据,根据这个handlerMap以及设置于其中的映射数据,可以方便地由URL请求得到它所对应的handler。有了这些准备工作,Spring MVC就可以等待HTTP请求的到来了。

3.2.使用HandlerMapping完成请求的映射处理
    继续通过SimpleUrlHandlerMapping的实现来分析HandlerMapping的接口方法getHandler,该方法会根据初始化时得到的映射关系来生成DispatcherServlet需要的HandlerExecutionChain,也就是说,这个getHandler方法是实际使用HandlerMapping完成请求的映射处理的地方。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  3.     Object handler = getHandlerInternal(request);  // ---------------->取得Handler的具体过程  
  4.   
  5.     // 使用默认的Handler,也就是"/"对应的Handler  
  6.     if (handler == null) {  
  7.         handler = getDefaultHandler();  
  8.     }  
  9.     if (handler == null) {  
  10.         return null;  
  11.     }  
  12.   
  13.     // 这里通过名称取出对应的Handler Bean  
  14.     if (handler instanceof String) {  
  15.         String handlerName = (String) handler;  
  16.         handler = getApplicationContext().getBean(handlerName);  
  17.     }  
  18.   
  19.     // 这里把Handler封装到HandlerExecutionChain中并加上拦截器 ------->  
  20.     HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);  
  21.   
  22.     if (CorsUtils.isCorsRequest(request)) {  
  23.         CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);  
  24.         CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);  
  25.         CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);  
  26.         executionChain = getCorsHandlerExecutionChain(request, executionChain, config);  
  27.     }  
  28.     return executionChain;  
  29. }  
  30.   
  31.   
  32. protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {  
  33.     HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?  
  34.             (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));  
  35.   
  36.     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  
  37.     for (HandlerInterceptor interceptor : this.adaptedInterceptors) {  
  38.         if (interceptor instanceof MappedInterceptor) {  
  39.             MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;  
  40.             if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {  
  41.                 chain.addInterceptor(mappedInterceptor.getInterceptor());  
  42.             }  
  43.         }  
  44.         else {  
  45.             chain.addInterceptor(interceptor);  
  46.         }  
  47.     }  
  48.     return chain;  
  49. }  
    取得handler的具体过程在getHandlerInternal方法中实现,这个方法接受HTTP请求作为参数,它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中,这个实现过程包括从HTTP请求中得到URL,并根据URL到urlMapping中获得handler。代码实现如下:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport  
  2. //          implements HandlerMapping, Ordered   
  3.   
  4.     protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;  
  5.   
  6.   
  7. //------------------------------------------  
  8. // public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {  
  9.   
  10.     @Override  
  11.     protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  
  12.         // 从request中得到请求的URL路径  
  13.         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);  
  14.   
  15.         /* 将得到的URL路径与Handler进行匹配,得到对应的Handler,如果没有对应的Handler, 
  16.           返回null, 这样默认的Handler会被使用 */  
  17.         Object handler = lookupHandler(lookupPath, request);  
  18.         if (handler == null) {  
  19.             // We need to care for the default handler directly, since we need to  
  20.             // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.  
  21.             Object rawHandler = null;  
  22.             if ("/".equals(lookupPath)) {  
  23.                 rawHandler = getRootHandler();  
  24.             }  
  25.             if (rawHandler == null) {  
  26.                 rawHandler = getDefaultHandler();  
  27.             }  
  28.             if (rawHandler != null) {  
  29.                 // Bean name or resolved handler?  
  30.                 if (rawHandler instanceof String) {  
  31.                     String handlerName = (String) rawHandler;  
  32.                     rawHandler = getApplicationContext().getBean(handlerName);  
  33.                 }  
  34.                 validateHandler(rawHandler, request);  
  35.                 handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);  
  36.             }  
  37.         }  
  38.         if (handler != null && logger.isDebugEnabled()) {  
  39.             logger.debug("Mapping [" + lookupPath + "] to " + handler);  
  40.         }  
  41.         else if (handler == null && logger.isTraceEnabled()) {  
  42.             logger.trace("No handler mapping found for [" + lookupPath + "]");  
  43.         }  
  44.         return handler;  
  45.     }  
  46.   
  47.     // lookupHandler根据URL路径启动在handlerMap中对handler的检索,并最终返回handler对象  
  48.     protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {  
  49.         // Direct match?  
  50.         Object handler = this.handlerMap.get(urlPath);  
  51.         if (handler != null) {  
  52.             // Bean name or resolved handler?  
  53.             if (handler instanceof String) {  
  54.                 String handlerName = (String) handler;  
  55.                 handler = getApplicationContext().getBean(handlerName);  
  56.             }  
  57.             validateHandler(handler, request);  
  58.             return buildPathExposingHandler(handler, urlPath, urlPath, null);  
  59.         }  
  60.         // Pattern match?  
  61.         List<String> matchingPatterns = new ArrayList<String>();  
  62.         for (String registeredPattern : this.handlerMap.keySet()) {  
  63.             if (getPathMatcher().match(registeredPattern, urlPath)) {  
  64.                 matchingPatterns.add(registeredPattern);  
  65.             }  
  66.             else if (useTrailingSlashMatch()) {  
  67.                 if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {  
  68.                     matchingPatterns.add(registeredPattern +"/");  
  69.                 }  
  70.             }  
  71.         }  
  72.         String bestPatternMatch = null;  
  73.         Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);  
  74.         if (!matchingPatterns.isEmpty()) {  
  75.             Collections.sort(matchingPatterns, patternComparator);  
  76.             if (logger.isDebugEnabled()) {  
  77.                 logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);  
  78.             }  
  79.             bestPatternMatch = matchingPatterns.get(0);  
  80.         }  
  81.         if (bestPatternMatch != null) {  
  82.             handler = this.handlerMap.get(bestPatternMatch);  
  83.             if (handler == null) {  
  84.                 Assert.isTrue(bestPatternMatch.endsWith("/"));  
  85.                 handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));  
  86.             }  
  87.             // Bean name or resolved handler?  
  88.             if (handler instanceof String) {  
  89.                 String handlerName = (String) handler;  
  90.                 handler = getApplicationContext().getBean(handlerName);  
  91.             }  
  92.             validateHandler(handler, request);  
  93.             String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);  
  94.   
  95.             // There might be multiple 'best patterns', let's make sure we have the correct URI template variables  
  96.             // for all of them  
  97.             Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();  
  98.             for (String matchingPattern : matchingPatterns) {  
  99.                 if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {  
  100.                     Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);  
  101.                     Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);  
  102.                     uriTemplateVariables.putAll(decodedVars);  
  103.                 }  
  104.             }  
  105.             if (logger.isDebugEnabled()) {  
  106.                 logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);  
  107.             }  
  108.             return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);  
  109.         }  
  110.         // No handler found...  
  111.         return null;  
  112.     }  
    经过这一系列对HTTP请求进行解析和匹配handler的过程,得到了与请求对应的handler处理器。在返回的handler中,已经完成了在HandlerExecutionChain中的封装工作,为handler对HTTP请求的响应做好了准备。然后,在MVC中,还有一个重要的问题:请求是怎样实现分发,从而到达对应的handler的呢?

3.Spring MVC对HTTP请求的分发处理
    重新回到DispatcherServlet,这个类不但建立了自己持有的IoC容器,还肩负着请求分发处理的重任。在MVC框架初始化完成以后,对HTTP请求的处理是在doService()方法中完成的,DispatcherServlet也是通过这个方法来响应HTTP的请求。然后,依据Spring MVC的使用,业务逻辑的调用入口是在handler的handler函数中实现的,这里是连接Spring MVC和应用业务逻辑实现的地方。DispatcherServlet的doService的实现代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  3.     if (logger.isDebugEnabled()) {  
  4.         String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";  
  5.         logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +  
  6.                 " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");  
  7.     }  
  8.   
  9.     // Keep a snapshot of the request attributes in case of an include,  
  10.     // to be able to restore the original attributes after the include.  
  11.     Map<String, Object> attributesSnapshot = null;  
  12.     if (WebUtils.isIncludeRequest(request)) {  
  13.         attributesSnapshot = new HashMap<String, Object>();  
  14.         Enumeration<?> attrNames = request.getAttributeNames();  
  15.         while (attrNames.hasMoreElements()) {  
  16.             String attrName = (String) attrNames.nextElement();  
  17.             if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {  
  18.                 attributesSnapshot.put(attrName, request.getAttribute(attrName));  
  19.             }  
  20.         }  
  21.     }  
  22.   
  23.     // Make framework objects available to handlers and view objects.  
  24.     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  
  25.     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);  
  26.     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);  
  27.     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());  
  28.   
  29.     FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);  
  30.     if (inputFlashMap != null) {  
  31.         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));  
  32.     }  
  33.     request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());  
  34.     request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);  
  35.   
  36.     try {  
  37.         doDispatch(request, response);  // --------------->  
  38.     }  
  39.     finally {  
  40.         if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {  
  41.             // Restore the original attribute snapshot, in case of an include.  
  42.             if (attributesSnapshot != null) {  
  43.                 restoreAttributesAfterInclude(request, attributesSnapshot);  
  44.             }  
  45.         }  
  46.     }  
  47. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.     HttpServletRequest processedRequest = request;  
  3.     HandlerExecutionChain mappedHandler = null;  
  4.     boolean multipartRequestParsed = false;  
  5.   
  6.     WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
  7.   
  8.     try {  
  9.         ModelAndView mv = null;  
  10.         Exception dispatchException = null;  
  11.   
  12.         try {  
  13.             processedRequest = checkMultipart(request);  
  14.             multipartRequestParsed = (processedRequest != request);  
  15.   
  16.             // Determine handler for the current request.  
  17.             mappedHandler = getHandler(processedRequest);  
  18.             if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  19.                 noHandlerFound(processedRequest, response);  
  20.                 return;  
  21.             }  
  22.   
  23.             // Determine handler adapter for the current request.  
  24.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  25.   
  26.             // Process last-modified header, if supported by the handler.  
  27.             String method = request.getMethod();  
  28.             boolean isGet = "GET".equals(method);  
  29.             if (isGet || "HEAD".equals(method)) {  
  30.                 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());  
  31.                 if (logger.isDebugEnabled()) {  
  32.                     logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);  
  33.                 }  
  34.                 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {  
  35.                     return;  
  36.                 }  
  37.             }  
  38.   
  39.             if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
  40.                 return;  
  41.             }  
  42.   
  43.             // Actually invoke the handler.  
  44.             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  45.   
  46.             if (asyncManager.isConcurrentHandlingStarted()) {  
  47.                 return;  
  48.             }  
  49.   
  50.             applyDefaultViewName(processedRequest, mv);  
  51.             mappedHandler.applyPostHandle(processedRequest, response, mv);  
  52.         }  
  53.         catch (Exception ex) {  
  54.             dispatchException = ex;  
  55.         }  
  56.         catch (Throwable err) {  
  57.             dispatchException = new NestedServletException("Handler dispatch failed", err);  
  58.         }  
  59.   
  60.         // --------------------->  
  61.         processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
  62.   
  63.     }  
  64.     catch (Exception ex) {  
  65.         triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
  66.     }  
  67.     catch (Throwable err) {  
  68.         triggerAfterCompletion(processedRequest, response, mappedHandler,  
  69.                 new NestedServletException("Handler processing failed", err));  
  70.     }  
  71.     finally {  
  72.         if (asyncManager.isConcurrentHandlingStarted()) {  
  73.             // Instead of postHandle and afterCompletion  
  74.             if (mappedHandler != null) {  
  75.                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);  
  76.             }  
  77.         }  
  78.         else {  
  79.             // Clean up any resources used by a multipart request.  
  80.             if (multipartRequestParsed) {  
  81.                 cleanupMultipart(processedRequest);  
  82.             }  
  83.         }  
  84.     }  
  85. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
  2.     for (HandlerMapping hm : this.handlerMappings) {  
  3.         if (logger.isTraceEnabled()) {  
  4.             logger.trace(  
  5.                     "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");  
  6.         }  
  7.         HandlerExecutionChain handler = hm.getHandler(request);  
  8.         if (handler != null) {  
  9.             return handler;  
  10.         }  
  11.     }  
  12.     return null;  
  13. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  
  2.         for (HandlerAdapter ha : this.handlerAdapters) {  
  3.             if (logger.isTraceEnabled()) {  
  4.                 logger.trace("Testing handler adapter [" + ha + "]");  
  5.             }  
  6.             if (ha.supports(handler)) {  // ------>  
  7.                 return ha;  
  8.             }  
  9.         }  
  10.         throw new ServletException("No adapter for handler [" + handler +  
  11.                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");  
  12.     }  
  13.   
  14.   
  15. // --------------------------------  
  16. public interface HandlerAdapter {  
  17.   
  18.     boolean supports(Object handler); // ------>  
  19.   
  20.     ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;  
  21.   
  22.     long getLastModified(HttpServletRequest request, Object handler);  
  23.   
  24. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // --------------------------------  
  2. public class SimpleControllerHandlerAdapter implements HandlerAdapter {  
  3.   
  4.     @Override  
  5.     public boolean supports(Object handler) {  
  6.         return (handler instanceof Controller);  
  7.     }  
  8.   
  9.     @Override  
  10.     public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)  
  11.             throws Exception {  
  12.   
  13.         return ((Controller) handler).handleRequest(request, response);  
  14.     }  
  15.   
  16.     @Override  
  17.     public long getLastModified(HttpServletRequest request, Object handler) {  
  18.         if (handler instanceof LastModified) {  
  19.             return ((LastModified) handler).getLastModified(request);  
  20.         }  
  21.         return -1L;  
  22.     }  
  23.   
  24. }  
    经过上面一系列的处理,得到了handler对象,接着就可以开始调用handler对象中的HTTP响应动作了。在handler中封装了应用业务逻辑,由这些逻辑对HTTP请求进行相应的处理,生成各种需要的数据,并把这些数据封装到ModelAndView对象中去,这个ModelAndView的数据封装是Spring MVC框架的要求。对handler来说,这些都是通过调用handler的handlerRequest方法来触发完成的。在得到ModelAndView对象以后,这个ModelAndView对象会被交给MVC模式中的视图类,由视图类对ModelAndView对象中的数据进行呈现。视图呈现的调用入口在DispatcherServlet的doDispatch方法中实现,它的调用入口是render方法。这个方法在下面介绍。

二、Spring MVC视图的呈现

1.DispatcherServlet视图呈现的设计

    前面分析了Spring MVC中的M(Model)和 C(Controller)相关的实现,其中的M大致对应成ModelAndView的生成,而C大致对应到DispatcherServlet和用户业务逻辑有关的handler实现。在Spring MVC框架中,DispatcherServlet起到了非常杧的作用,是整个MVC框架的调用枢纽。对于下面关心的视图呈现功能,它的调用入口同样在DispatcherServlet中的doDispatch方法中实现。具体来说,在DispatcherServlet中,对视图呈现的处理是在render方法调用中完成的,代码如下。
    为了完成视图的呈现工作,需要从ModelAndViewc对象中取得视图对象,然后调用视图对象的render方法,由这个视图对象来完成特定的视图呈现工作。同时,由于是在Web的环境中,因此视图对象的呈现往往需要完成与HTTP请求和响应相关的处理,这些对象会作为参数传到视图对象的render方法中,供render方法使用。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  2.     // Determine locale for request and apply it to the response.  
  3.     Locale locale = this.localeResolver.resolveLocale(request);  
  4.     response.setLocale(locale);  
  5.   
  6.     View view;  
  7.     if (mv.isReference()) {  
  8.         // We need to resolve the view name. --------------->  
  9.         view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);  
  10.         if (view == null) {  
  11.             throw new ServletException("Could not resolve view with name '" + mv.getViewName() +  
  12.                     "' in servlet with name '" + getServletName() + "'");  
  13.         }  
  14.     }  
  15.     else {  
  16.         // No need to lookup: the ModelAndView object contains the actual View object.  
  17.         view = mv.getView();  
  18.         if (view == null) {  
  19.             throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +  
  20.                     "View object in servlet with name '" + getServletName() + "'");  
  21.         }  
  22.     }  
  23.   
  24.     // Delegate to the View object for rendering.  
  25.     if (logger.isDebugEnabled()) {  
  26.         logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");  
  27.     }  
  28.     try {  
  29.         if (mv.getStatus() != null) {  
  30.             response.setStatus(mv.getStatus().value());  
  31.         }  
  32.         view.render(mv.getModelInternal(), request, response);  
  33.     }  
  34.     catch (Exception ex) {  
  35.         if (logger.isDebugEnabled()) {  
  36.             logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +  
  37.                     getServletName() + "'", ex);  
  38.         }  
  39.         throw ex;  
  40.     }  
  41. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1.     protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,  
  2.             HttpServletRequest request) throws Exception {  
  3.   
  4.         // 调用ViewResolver进行解析  
  5.         for (ViewResolver viewResolver : this.viewResolvers) {   
  6.             // ----------------------->  
  7.             View view = viewResolver.resolveViewName(viewName, locale);  
  8.             if (view != null) {  
  9.                 return view;  
  10.             }  
  11.         }  
  12.         return null;  
  13.     }  
  14.   
  15.   
  16. //--------------------------------------------  
  17. public interface ViewResolver {  
  18.   
  19.     View resolveViewName(String viewName, Locale locale) throws Exception;  
  20.   
  21. }  


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {  
  2.   
  3.     private int order = Integer.MAX_VALUE;  // default: same as non-Ordered  
  4.   
  5.   
  6.     public void setOrder(int order) {  
  7.         this.order = order;  
  8.     }  
  9.   
  10.     @Override  
  11.     public int getOrder() {  
  12.         return this.order;  
  13.     }  
  14.   
  15.   
  16.     @Override  
  17.     public View resolveViewName(String viewName, Locale locale) throws BeansException {  
  18.         ApplicationContext context = getApplicationContext();  
  19.         if (!context.containsBean(viewName)) {  
  20.             if (logger.isDebugEnabled()) {  
  21.                 logger.debug("No matching bean found for view name '" + viewName + "'");  
  22.             }  
  23.             return null;  
  24.         }  
  25.           
  26.         if (!context.isTypeMatch(viewName, View.class)) {  
  27.             if (logger.isDebugEnabled()) {  
  28.                 logger.debug("Found matching bean for view name '" + viewName +  
  29.                         "' - to be ignored since it does not implement View");  
  30.             }  
  31.             return null;  
  32.         }  
  33.         return context.getBean(viewName, View.class);  
  34.     }  
  35.   
  36. }  
    这样就得到了需要的View对象,下面介绍View。下面是View的继承体系

1.JSP视图的实现
    使用JSP的页面作为Web UI,是使用Java设计Web应用比较常见的选择之一,如果在JSP使用Jstl(JSP Standard Tag Library)来丰富JSP的功能,在Spring MVC中就需要使用JstlView来作为View对象,从而对数据进行视图呈现。JstlView没有实现render方法,使用的render方法是在它的基类AbstractView中实现的,实现如下:


[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware   
  2.   
  3.     @Override  
  4.     public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  5.         if (logger.isTraceEnabled()) {  
  6.             logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +  
  7.                 " and static attributes " + this.staticAttributes);  
  8.         }  
  9.   
  10.         // 这里把所有的相关信息都收集到一个Map里 ------》  
  11.         Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);  
  12.         prepareResponse(request, response);  
  13.   
  14.         // 展现模型数据到视图的调用方法 -------->这是一个模板方法  
  15.         renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);  
  16.     }  
  17.   
  18.   
  19.     protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,  
  20.             HttpServletResponse response) {  
  21.   
  22.         @SuppressWarnings("unchecked")  
  23.         Map<String, Object> pathVars = (this.exposePathVariables ?  
  24.                 (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);  
  25.   
  26.         // Consolidate static and dynamic model attributes.  
  27.         int size = this.staticAttributes.size();  
  28.         size += (model != null ? model.size() : 0);  
  29.         size += (pathVars != null ? pathVars.size() : 0);  
  30.   
  31.         Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);  
  32.         mergedModel.putAll(this.staticAttributes);  
  33.         if (pathVars != null) {  
  34.             mergedModel.putAll(pathVars);  
  35.         }  
  36.         if (model != null) {  
  37.             mergedModel.putAll(model);  
  38.         }  
  39.   
  40.         // Expose RequestContext?  
  41.         if (this.requestContextAttribute != null) {  
  42.             mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));  
  43.         }  
  44.   
  45.         return mergedModel;  
  46.     }  
  47.   
  48.   
  49.     protected abstract void renderMergedOutputModel(  
  50.             Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;  

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // ----------------------------------------------------  
  2. // public class InternalResourceView extends AbstractUrlBasedView   
  3.   
  4.     @Override  
  5.     protected void renderMergedOutputModel(  
  6.             Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {  
  7.   
  8.         // 1.对模型数据进行处理,把模型对象存放到ServletContext中  
  9.         exposeModelAsRequestAttributes(model, request); // 1.--------------->  
  10.   
  11.         // Expose helpers as request attributes, if any.  
  12.         // 这是一个模板方法,在JstlView中实现  
  13.         exposeHelpers(request);  2.--------------->  
  14.   
  15.         // 获取InternalResource定义的内部资源路径  
  16.         String dispatcherPath = prepareForRendering(request, response); // ----------->  
  17.   
  18.         // 把请求转发到前面获取的内部资源路径中去  
  19.         RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);  
  20.         if (rd == null) {  
  21.             throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +  
  22.                     "]: Check that the corresponding file exists within your web application archive!");  
  23.         }  
  24.   
  25.         // If already included or response already committed, perform include, else forward.  
  26.         if (useInclude(request, response)) {  
  27.             response.setContentType(getContentType());  
  28.             if (logger.isDebugEnabled()) {  
  29.                 logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");  
  30.             }  
  31.             rd.include(request, response);  
  32.         }  
  33.   
  34.         else {  
  35.             // Note: The forwarded resource is supposed to determine the content type itself.  
  36.             if (logger.isDebugEnabled()) {  
  37.                 logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");  
  38.             }  
  39.             rd.forward(request, response);  
  40.         }  
  41.     }  
  42.   
  43.     protected void exposeHelpers(HttpServletRequest request) throws Exception {  
  44.     }  
  45.   
  46. // 1.-------------------------------------  
  47. // public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware  
  48.   
  49.     /* 这个方法把ModelAndView中的模型数据和其他请求数据都放到HttpServletRequest的属性中去, 
  50.       这样就可以通过HttpServletRequest的属性得到和使用这些数据了。*/  
  51.     protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {  
  52.         for (Map.Entry<String, Object> entry : model.entrySet()) {  
  53.             String modelName = entry.getKey();  
  54.             Object modelValue = entry.getValue();  
  55.             if (modelValue != null) {  
  56.                 request.setAttribute(modelName, modelValue);  
  57.                 if (logger.isDebugEnabled()) {  
  58.                     logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +  
  59.                             "] to request in view with name '" + getBeanName() + "'");  
  60.                 }  
  61.             }  
  62.             else {  
  63.                 request.removeAttribute(modelName);  
  64.                 if (logger.isDebugEnabled()) {  
  65.                     logger.debug("Removed model object '" + modelName +  
  66.                             "' from request in view with name '" + getBeanName() + "'");  
  67.                 }  
  68.             }  
  69.         }  
  70.     }  
  71.   
  72. // 2.-------------------------------------  
  73. // public class JstlView extends InternalResourceView  
  74.   
  75.     @Override  
  76.     protected void exposeHelpers(HttpServletRequest request) throws Exception {  
  77.         if (this.messageSource != null) {  
  78.             JstlUtils.exposeLocalizationContext(request, this.messageSource);  
  79.         }  
  80.         else {  
  81.             JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));  
  82.         }  
  83.     }  
     在这些处理的基础 上,实际的数据到页面的输出是由InternalResourceView来完成的,render完成资源的重定向处理。需要做的是,在得到实际视图的InternalResource路径以后,把请求转发到资源中去。如何得到资源的路径呢?在InternalResourceView中可以看到调用,如下代码。在这里可以看到,从request取得URL的路径,并对取得的路径进行了相应的处理。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)  
  2.         throws Exception {  
  3.   
  4.     String path = getUrl();  
  5.     if (this.preventDispatchLoop) {  
  6.         String uri = request.getRequestURI();  
  7.         if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {  
  8.             throw new ServletException("Circular view path [" + path + "]: would dispatch back " +  
  9.                     "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +  
  10.                     "(Hint: This may be the result of an unspecified view, due to default view name generation.)");  
  11.         }  
  12.     }  
  13.     return path;  
  14. }  
    在得到URL路径之后,使用RequestDispatcher把请求转发到这个资源上,就完成了带JSTL的JSP页面的展现。
转载出处:http://blog.csdn.net/oChangWen/article/details/61616464

转载出处
0 0
原创粉丝点击