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

    xiaoxiao2021-03-25  67

    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   // public abstract class HttpServletBean extends HttpServlet   //      implements EnvironmentCapable, EnvironmentAware           @Override       public final void init() throws ServletException {           if (logger.isDebugEnabled()) {               logger.debug("Initializing servlet '" + getServletName() + "'");           }              // 获取Servlet的初始化参数,对Bean属性进行配置           try {               PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);               BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);               ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());               bw.registerCustomEditor(Resource.classnew ResourceEditor(resourceLoader, getEnvironment()));               initBeanWrapper(bw);               bw.setPropertyValues(pvs, true);           }           catch (BeansException ex) {               logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);               throw ex;           }              // 调用子类的方法进行具体的初始化,模板方法           initServletBean();  // ---------------->              if (logger.isDebugEnabled()) {               logger.debug("Servlet '" + getServletName() + "' configured successfully");           }       }          protected void initServletBean() throws ServletException {       }      // ------------------------------------   // FrameworkServlet          @Override       protected final void initServletBean() throws ServletException {           getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");           if (this.logger.isInfoEnabled()) {               this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");           }           long startTime = System.currentTimeMillis();              // 这里初始化上下文           try {               this.webApplicationContext = initWebApplicationContext(); // ----------->               initFrameworkServlet();           }           catch (ServletException ex) {               this.logger.error("Context initialization failed", ex);               throw ex;           }           catch (RuntimeException ex) {               this.logger.error("Context initialization failed", ex);               throw ex;           }              if (this.logger.isInfoEnabled()) {               long elapsedTime = System.currentTimeMillis() - startTime;               this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +                       elapsedTime + " ms");           }       }                protected WebApplicationContext initWebApplicationContext() {              // 这里调用WebApplicationContextUtils静态类来得到根上下文,           // 这个根上下文是保存在ServletContext中的,           // 使用这个根上下文作为当前MVC上下文的双亲上下文            WebApplicationContext rootContext =                   WebApplicationContextUtils.getWebApplicationContext(getServletContext()); // ---->           WebApplicationContext wac = null;              if (this.webApplicationContext != null) {               wac = this.webApplicationContext;               if (wac instanceof ConfigurableWebApplicationContext) {                   ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                   if (!cwac.isActive()) {                       if (cwac.getParent() == null) {                           cwac.setParent(rootContext);                       }                       configureAndRefreshWebApplicationContext(cwac);                   }               }           }           if (wac == null) {               wac = findWebApplicationContext();           }           if (wac == null) {               // ------------------>               wac = createWebApplicationContext(rootContext);           }              if (!this.refreshEventReceived) {               onRefresh(wac);   // -------->           }              // 把当前建立的上下文存到ServletContext中去,注意使用的属性名是和当前Servlet名相关的           if (this.publishContext) {               String attrName = getServletContextAttributeName();               getServletContext().setAttribute(attrName, wac);               if (this.logger.isDebugEnabled()) {                   this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +                           "' as ServletContext attribute with name [" + attrName + "]");               }           }              return wac;       }       在这里, 这个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   // public abstract class WebApplicationContextUtils           public static WebApplicationContext getWebApplicationContext(ServletContext sc) {           return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);       }   -------------------------------     回到FrameworkServlet的实现中来看一下 DispatcherServlet的上下文是怎样建立的。这个建立过程与前面建立根上下文的过程非常类似,如下代理。建立DispatcherServlet的上下文,需要把根上下文作为参数传递给它。然后使用反射技术来实例化上下文对象,并为它设置参数。根据默认的配置,这个 上下文对象也是XmlWebApplicationContext对象,这个类型是在DEFAULT_CONTEXT_CLASS 参数中设置好并允许BeanUtils使用的。在实例化结束以后,需要为这个上下文对象设置好一些基本的配置,这些配置包括它的双亲上下文、Bean定义配置的文件位置等。完成这些配置以后,最后通过调用IoC容器的refresh方法来完成IoC容器的最终初始化,这和前面我们对IoC容器实现原理的分析 所看到的IoC容器初始化过程是一致的。 [java]  view plain  copy   //FrameworkServlet 建立WebApplicationContext          public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;          private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;          public Class<?> getContextClass() {  // -->           return this.contextClass;       }          protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {           return createWebApplicationContext((ApplicationContext) parent);       }          protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {              Class<?> contextClass = getContextClass();  // -----|这里得到的是XmlWebApplicationContext              if (this.logger.isDebugEnabled()) {               this.logger.debug("Servlet with name '" + getServletName() +                       "' will try to create custom WebApplicationContext context of class '" +                       contextClass.getName() + "'" + ", using parent context [" + parent + "]");           }           if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {               throw new ApplicationContextException(                       "Fatal initialization error in servlet with name '" + getServletName() +                       "': custom WebApplicationContext class [" + contextClass.getName() +                       "] is not of type ConfigurableWebApplicationContext");           }              // DispatcherServlet中使用的IoC容器是XmlWebApplicationContext           ConfigurableWebApplicationContext wac =                   (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);              wac.setEnvironment(getEnvironment());           wac.setParent(parent);           wac.setConfigLocation(getContextConfigLocation());              configureAndRefreshWebApplicationContext(wac); // ---->              return wac;       }             protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {           if (ObjectUtils.identityToString(wac).equals(wac.getId())) {               if (this.contextId != null) {                   wac.setId(this.contextId);               }               else {                   wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +                           ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());               }           }              wac.setServletContext(getServletContext());           wac.setServletConfig(getServletConfig());           wac.setNamespace(getNamespace());           wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));              ConfigurableEnvironment env = wac.getEnvironment();           if (env instanceof ConfigurableWebEnvironment) {               ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());           }              postProcessWebApplicationContext(wac);           applyInitializers(wac);           // 这里同样是调用refresh来调用容器的初始化过程的           wac.refresh();  // ------->       }       这个时候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   //FrameworkServlet          protected void onRefresh(ApplicationContext context) {           // For subclasses: do nothing by default.       }      //--------------------------------------   // public class DispatcherServlet extends FrameworkServlet {          @Override       protected void onRefresh(ApplicationContext context) {           initStrategies(context);       }          protected void initStrategies(ApplicationContext context) {           initMultipartResolver(context);  // 1.处理文件上传服务           initLocaleResolver(context);  // 2.处理国际化           initThemeResolver(context);  // 3.处理主题           initHandlerMappings(context);  // 4.请求映射关系           initHandlerAdapters(context);  // 5.根据Hander类型定义不同处理adapter           initHandlerExceptionResolvers(context);  // 6.异常处理           initRequestToViewNameTranslator(context);  // 7.将viewname转换成想要的格式,如jsp           initViewResolvers(context);  // 8.view解析成页面           initFlashMapManager(context);  // 9.       }       对于具体的初始化过程,根据上面的方法名称,很容易理解。以HanderMapping为例来说明这个initHanderMappings过程。这里的Mapping关系的作用是,为HTTP请求找到相应 的Controller控制器,从而利用这些控制器Controller去完成设计好的数据处理工作。HandlerMappings完成对MVC中Controller的定义和配置,只不过在Web这个特定的应用环境中,这些控制器是与具体的HTTP请求相对应的。DispatcherServlet中HandlerMappings初始化过程的具体实现如下。在HandlerMapping初始化的过程中,把在Bean配置文件中配置好的handlerMapping从IoC容器中取得。 [java]  view plain  copy   // 对HandlerMapping的初始化          private void initHandlerMappings(ApplicationContext context) {           this.handlerMappings = null;              /* 这里导入所有的HandlerMapping Bean,这些Bean可以在当前的DispatcherServlet的IoC容器中,            也可能在其双亲上下文中,这个detectAllHandlerMappings的默认值设为true,            即默认地从所有的IoC容器中取*/           if (this.detectAllHandlerMappings) {               Map<String, HandlerMapping> matchingBeans =                       BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.classtruefalse);               if (!matchingBeans.isEmpty()) {                   this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());                   AnnotationAwareOrderComparator.sort(this.handlerMappings);               }           }           else {  // 可以根据名称从当前的IoC容器中通过getBean获取handlerMapping               try {                   HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);                   this.handlerMappings = Collections.singletonList(hm);               }               catch (NoSuchBeanDefinitionException ex) {                   // Ignore, we'll add a default HandlerMapping later.               }           }              /* 如果没有找到handerMappings,那么需要为Servlet设定默认的handlerMappings,            这些默认的值可以设置在DispatcherServlet.properties*/           if (this.handlerMappings == null) {               this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);               if (logger.isDebugEnabled()) {                   logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");               }           }       }       经过以上的读取过程,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   public interface HandlerMapping {          String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";          String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";          String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";          String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";          String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";          String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";          /* 调用这个方法实际上返回的是一个HandlerExecutionChain,这是典型的Commoand的模式的使用,        这个HandlerExecutionChain不但持有handler本身,还包括了处理这个HTTP请求相关的拦截器。*/       HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;      }       这个HandlerExecutionChain的实现看起来比较简洁,它持有一个拦截器链和一个handler对象,这个handler对象实际上就是HTTP请求对应的Controller,在持有这个handler对象的同时,还在HandlerExecutionChain中设置了拦截器链,通过这个拦截器链中的拦截器,可以为handler对象提供功能的增强。要完成这些工作,需要对拦截器链和handler都进行配置,这些配置都是在这个类的初始化函数中完成的。     为了维护这个拦截器和handler, HandlerExecutionChain还提供了一系列与拦截器链维护相关一些操作,比如可以为拦截器链增加拦截器的addInterceptor方法等。 [java]  view plain  copy   // ----------------HandlerExecutionChain的实现-------------------      public class HandlerExecutionChain {          private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);          private final Object handler;       private HandlerInterceptor[] interceptors;       private List<HandlerInterceptor> interceptorList;          private int interceptorIndex = -1;             public HandlerExecutionChain(Object handler) {           this(handler, (HandlerInterceptor[]) null);       }          public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {           if (handler instanceof HandlerExecutionChain) {               HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;               this.handler = originalChain.getHandler();               this.interceptorList = new ArrayList<HandlerInterceptor>();               CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);               CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);           }           else {               this.handler = handler;               this.interceptors = interceptors;           }       }          public Object getHandler() {           return this.handler;       }          public void addInterceptor(HandlerInterceptor interceptor) {           initInterceptorList().add(interceptor);       }          public void addInterceptors(HandlerInterceptor... interceptors) {           if (!ObjectUtils.isEmpty(interceptors)) {               initInterceptorList().addAll(Arrays.asList(interceptors));           }       }          private List<HandlerInterceptor> initInterceptorList() {           if (this.interceptorList == null) {               this.interceptorList = new ArrayList<HandlerInterceptor>();               if (this.interceptors != null) {                   // An interceptor array specified through the constructor                   this.interceptorList.addAll(Arrays.asList(this.interceptors));               }           }           this.interceptors = null;           return this.interceptorList;       }          public HandlerInterceptor[] getInterceptors() {           if (this.interceptors == null && this.interceptorList != null) {               this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);           }           return this.interceptors;       }          boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {           HandlerInterceptor[] interceptors = getInterceptors();           if (!ObjectUtils.isEmpty(interceptors)) {               for (int i = 0; i < interceptors.length; i++) {                   HandlerInterceptor interceptor = interceptors[i];                   if (!interceptor.preHandle(request, response, this.handler)) {                       triggerAfterCompletion(request, response, null);                       return false;                   }                   this.interceptorIndex = i;               }           }           return true;       }          void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {           HandlerInterceptor[] interceptors = getInterceptors();           if (!ObjectUtils.isEmpty(interceptors)) {               for (int i = interceptors.length - 1; i >= 0; i--) {                   HandlerInterceptor interceptor = interceptors[i];                   interceptor.postHandle(request, response, this.handler, mv);               }           }       }          void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)               throws Exception {              HandlerInterceptor[] interceptors = getInterceptors();           if (!ObjectUtils.isEmpty(interceptors)) {               for (int i = this.interceptorIndex; i >= 0; i--) {                   HandlerInterceptor interceptor = interceptors[i];                   try {                       interceptor.afterCompletion(request, response, this.handler, ex);                   }                   catch (Throwable ex2) {                       logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);                   }               }           }       }          void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {           HandlerInterceptor[] interceptors = getInterceptors();           if (!ObjectUtils.isEmpty(interceptors)) {               for (int i = interceptors.length - 1; i >= 0; i--) {                   if (interceptors[i] instanceof AsyncHandlerInterceptor) {                       try {                           AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];                           asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);                       }                       catch (Throwable ex) {                           logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);                       }                   }               }           }       }          @Override       public String toString() {           if (this.handler == null) {               return "HandlerExecutionChain with no handler";           }           StringBuilder sb = new StringBuilder();           sb.append("HandlerExecutionChain with handler [").append(this.handler).append("]");           if (!CollectionUtils.isEmpty(this.interceptorList)) {               sb.append(" and ").append(this.interceptorList.size()).append(" interceptor");               if (this.interceptorList.size() > 1) {                   sb.append("s");               }           }           return sb.toString();       }      }       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   public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {          private final Map<String, Object> urlMap = new LinkedHashMap<String, Object>();          public void setMappings(Properties mappings) {           CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);       }             public void setUrlMap(Map<String, ?> urlMap) {           this.urlMap.putAll(urlMap);       }       public Map<String, ?> getUrlMap() {           return this.urlMap;       }          // 注册handler       @Override       public void initApplicationContext() throws BeansException {           super.initApplicationContext();           registerHandlers(this.urlMap);       }          protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {           if (urlMap.isEmpty()) {               logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");           }           else {               // 这里对Bean的配置进行解析,然后调用基类的registerHandler完成注册               for (Map.Entry<String, Object> entry : urlMap.entrySet()) {                   String url = entry.getKey();                   Object handler = entry.getValue();                   // Prepend with slash if not already present.                   if (!url.startsWith("/")) {                       url = "/" + url;                   }                   // Remove whitespace from handler bean name.                   if (handler instanceof String) {                       handler = ((String) handler).trim();                   }                   registerHandler(url, handler);  // -------->               }           }       }      }   这个SimpleUrlHandlerMapping注册过程的完成,很大一部分需要它的基类来配合,这个基类就是AbstractUrlHandlerMapping. [java]  view plain  copy   //----------------------------------------   // public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {          // 保存了URL请求和Controller的映射关系       private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();          /* 在这个处理过程中,如果使用Bean的名字作为映射,那么直接从容器中获取这个HTTP映射对应的Bean,        然后还要对不同的URL配置进行解析处理,比如在HTTP请求中配置成"/"和配置符"/*"的URL,以及正常的URL请求,        完成这个解析处理后,会把URL和handler作为键值对应放到一个handlerMap中去*/       protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {           Assert.notNull(urlPath, "URL path must not be null");           Assert.notNull(handler, "Handler object must not be null");           Object resolvedHandler = handler;              // 如果直接用bean名称进行映射,那就直接从窗口中获取handler           if (!this.lazyInitHandlers && handler instanceof String) {               String handlerName = (String) handler;               if (getApplicationContext().isSingleton(handlerName)) {                   resolvedHandler = getApplicationContext().getBean(handlerName);               }           }              Object mappedHandler = this.handlerMap.get(urlPath);           if (mappedHandler != null) {               if (mappedHandler != resolvedHandler) {                   throw new IllegalStateException(                           "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +                           "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");               }           }           else { // 处理URL是"/"的映射,把这个"/"映射的controller设置到rootHandler中               if (urlPath.equals("/")) {                   if (logger.isInfoEnabled()) {                       logger.info("Root mapping to " + getHandlerDescription(handler));                   }                   setRootHandler(resolvedHandler);               }               // 处理URL是"/*"的映射,把这个"/"映射的Controler设置到defaultHandler中               else if (urlPath.equals("/*")) {                   if (logger.isInfoEnabled()) {                       logger.info("Default mapping to " + getHandlerDescription(handler));                   }                   setDefaultHandler(resolvedHandler);               }               // 处理正常的URL映射,设置handlerMap的key和value,分别对应于URL和映射的controller               else {                   this.handlerMap.put(urlPath, resolvedHandler);                   if (logger.isInfoEnabled()) {                       logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));                   }               }           }       }       这个配置好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   @Override   public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {       Object handler = getHandlerInternal(request);  // ---------------->取得Handler的具体过程          // 使用默认的Handler,也就是"/"对应的Handler       if (handler == null) {           handler = getDefaultHandler();       }       if (handler == null) {           return null;       }          // 这里通过名称取出对应的Handler Bean       if (handler instanceof String) {           String handlerName = (String) handler;           handler = getApplicationContext().getBean(handlerName);       }          // 这里把Handler封装到HandlerExecutionChain中并加上拦截器 ------->       HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);          if (CorsUtils.isCorsRequest(request)) {           CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);           CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);           CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);           executionChain = getCorsHandlerExecutionChain(request, executionChain, config);       }       return executionChain;   }         protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {       HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?               (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));          String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);       for (HandlerInterceptor interceptor : this.adaptedInterceptors) {           if (interceptor instanceof MappedInterceptor) {               MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;               if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {                   chain.addInterceptor(mappedInterceptor.getInterceptor());               }           }           else {               chain.addInterceptor(interceptor);           }       }       return chain;   }       取得handler的具体过程在getHandlerInternal方法中实现,这个方法接受HTTP请求作为参数,它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中,这个实现过程包括从HTTP请求中得到URL,并根据URL到urlMapping中获得handler。代码实现如下: [java]  view plain  copy   // public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport   //          implements HandlerMapping, Ordered           protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;         //------------------------------------------   // public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {          @Override       protected Object getHandlerInternal(HttpServletRequest request) throws Exception {           // 从request中得到请求的URL路径           String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);              /* 将得到的URL路径与Handler进行匹配,得到对应的Handler,如果没有对应的Handler,            返回null, 这样默认的Handler会被使用 */           Object handler = lookupHandler(lookupPath, request);           if (handler == null) {               // We need to care for the default handler directly, since we need to               // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.               Object rawHandler = null;               if ("/".equals(lookupPath)) {                   rawHandler = getRootHandler();               }               if (rawHandler == null) {                   rawHandler = getDefaultHandler();               }               if (rawHandler != null) {                   // Bean name or resolved handler?                   if (rawHandler instanceof String) {                       String handlerName = (String) rawHandler;                       rawHandler = getApplicationContext().getBean(handlerName);                   }                   validateHandler(rawHandler, request);                   handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);               }           }           if (handler != null && logger.isDebugEnabled()) {               logger.debug("Mapping [" + lookupPath + "] to " + handler);           }           else if (handler == null && logger.isTraceEnabled()) {               logger.trace("No handler mapping found for [" + lookupPath + "]");           }           return handler;       }          // lookupHandler根据URL路径启动在handlerMap中对handler的检索,并最终返回handler对象       protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {           // Direct match?           Object handler = this.handlerMap.get(urlPath);           if (handler != null) {               // Bean name or resolved handler?               if (handler instanceof String) {                   String handlerName = (String) handler;                   handler = getApplicationContext().getBean(handlerName);               }               validateHandler(handler, request);               return buildPathExposingHandler(handler, urlPath, urlPath, null);           }           // Pattern match?           List<String> matchingPatterns = new ArrayList<String>();           for (String registeredPattern : this.handlerMap.keySet()) {               if (getPathMatcher().match(registeredPattern, urlPath)) {                   matchingPatterns.add(registeredPattern);               }               else if (useTrailingSlashMatch()) {                   if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {                       matchingPatterns.add(registeredPattern +"/");                   }               }           }           String bestPatternMatch = null;           Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);           if (!matchingPatterns.isEmpty()) {               Collections.sort(matchingPatterns, patternComparator);               if (logger.isDebugEnabled()) {                   logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);               }               bestPatternMatch = matchingPatterns.get(0);           }           if (bestPatternMatch != null) {               handler = this.handlerMap.get(bestPatternMatch);               if (handler == null) {                   Assert.isTrue(bestPatternMatch.endsWith("/"));                   handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));               }               // Bean name or resolved handler?               if (handler instanceof String) {                   String handlerName = (String) handler;                   handler = getApplicationContext().getBean(handlerName);               }               validateHandler(handler, request);               String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);                  // There might be multiple 'best patterns', let's make sure we have the correct URI template variables               // for all of them               Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();               for (String matchingPattern : matchingPatterns) {                   if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {                       Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);                       Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);                       uriTemplateVariables.putAll(decodedVars);                   }               }               if (logger.isDebugEnabled()) {                   logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);               }               return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);           }           // No handler found...           return null;       }       经过这一系列对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   @Override   protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {       if (logger.isDebugEnabled()) {           String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";           logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +                   " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");       }          // Keep a snapshot of the request attributes in case of an include,       // to be able to restore the original attributes after the include.       Map<String, Object> attributesSnapshot = null;       if (WebUtils.isIncludeRequest(request)) {           attributesSnapshot = new HashMap<String, Object>();           Enumeration<?> attrNames = request.getAttributeNames();           while (attrNames.hasMoreElements()) {               String attrName = (String) attrNames.nextElement();               if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {                   attributesSnapshot.put(attrName, request.getAttribute(attrName));               }           }       }          // Make framework objects available to handlers and view objects.       request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());       request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);       request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);       request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());          FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);       if (inputFlashMap != null) {           request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));       }       request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());       request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);          try {           doDispatch(request, response);  // --------------->       }       finally {           if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {               // Restore the original attribute snapshot, in case of an include.               if (attributesSnapshot != null) {                   restoreAttributesAfterInclude(request, attributesSnapshot);               }           }       }   }  

    [java]  view plain  copy   protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {       HttpServletRequest processedRequest = request;       HandlerExecutionChain mappedHandler = null;       boolean multipartRequestParsed = false;          WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);          try {           ModelAndView mv = null;           Exception dispatchException = null;              try {               processedRequest = checkMultipart(request);               multipartRequestParsed = (processedRequest != request);                  // Determine handler for the current request.               mappedHandler = getHandler(processedRequest);               if (mappedHandler == null || mappedHandler.getHandler() == null) {                   noHandlerFound(processedRequest, response);                   return;               }                  // Determine handler adapter for the current request.               HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());                  // Process last-modified header, if supported by the handler.               String method = request.getMethod();               boolean isGet = "GET".equals(method);               if (isGet || "HEAD".equals(method)) {                   long lastModified = ha.getLastModified(request, mappedHandler.getHandler());                   if (logger.isDebugEnabled()) {                       logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);                   }                   if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {                       return;                   }               }                  if (!mappedHandler.applyPreHandle(processedRequest, response)) {                   return;               }                  // Actually invoke the handler.               mv = ha.handle(processedRequest, response, mappedHandler.getHandler());                  if (asyncManager.isConcurrentHandlingStarted()) {                   return;               }                  applyDefaultViewName(processedRequest, mv);               mappedHandler.applyPostHandle(processedRequest, response, mv);           }           catch (Exception ex) {               dispatchException = ex;           }           catch (Throwable err) {               dispatchException = new NestedServletException("Handler dispatch failed", err);           }              // --------------------->           processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);          }       catch (Exception ex) {           triggerAfterCompletion(processedRequest, response, mappedHandler, ex);       }       catch (Throwable err) {           triggerAfterCompletion(processedRequest, response, mappedHandler,                   new NestedServletException("Handler processing failed", err));       }       finally {           if (asyncManager.isConcurrentHandlingStarted()) {               // Instead of postHandle and afterCompletion               if (mappedHandler != null) {                   mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);               }           }           else {               // Clean up any resources used by a multipart request.               if (multipartRequestParsed) {                   cleanupMultipart(processedRequest);               }           }       }   }  

    [java]  view plain  copy   protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {       for (HandlerMapping hm : this.handlerMappings) {           if (logger.isTraceEnabled()) {               logger.trace(                       "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");           }           HandlerExecutionChain handler = hm.getHandler(request);           if (handler != null) {               return handler;           }       }       return null;   }  

    [java]  view plain  copy       protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {           for (HandlerAdapter ha : this.handlerAdapters) {               if (logger.isTraceEnabled()) {                   logger.trace("Testing handler adapter [" + ha + "]");               }               if (ha.supports(handler)) {  // ------>                   return ha;               }           }           throw new ServletException("No adapter for handler [" + handler +                   "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");       }         // --------------------------------   public interface HandlerAdapter {          boolean supports(Object handler); // ------>          ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;          long getLastModified(HttpServletRequest request, Object handler);      }  

    [java]  view plain  copy   // --------------------------------   public class SimpleControllerHandlerAdapter implements HandlerAdapter {          @Override       public boolean supports(Object handler) {           return (handler instanceof Controller);       }          @Override       public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)               throws Exception {              return ((Controller) handler).handleRequest(request, response);       }          @Override       public long getLastModified(HttpServletRequest request, Object handler) {           if (handler instanceof LastModified) {               return ((LastModified) handler).getLastModified(request);           }           return -1L;       }      }       经过上面一系列的处理,得到了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   protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {       // Determine locale for request and apply it to the response.       Locale locale = this.localeResolver.resolveLocale(request);       response.setLocale(locale);          View view;       if (mv.isReference()) {           // We need to resolve the view name. --------------->           view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);           if (view == null) {               throw new ServletException("Could not resolve view with name '" + mv.getViewName() +                       "' in servlet with name '" + getServletName() + "'");           }       }       else {           // No need to lookup: the ModelAndView object contains the actual View object.           view = mv.getView();           if (view == null) {               throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +                       "View object in servlet with name '" + getServletName() + "'");           }       }          // Delegate to the View object for rendering.       if (logger.isDebugEnabled()) {           logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");       }       try {           if (mv.getStatus() != null) {               response.setStatus(mv.getStatus().value());           }           view.render(mv.getModelInternal(), request, response);       }       catch (Exception ex) {           if (logger.isDebugEnabled()) {               logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +                       getServletName() + "'", ex);           }           throw ex;       }   }  

    [java]  view plain  copy       protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,               HttpServletRequest request) throws Exception {              // 调用ViewResolver进行解析           for (ViewResolver viewResolver : this.viewResolvers) {                // ----------------------->               View view = viewResolver.resolveViewName(viewName, locale);               if (view != null) {                   return view;               }           }           return null;       }         //--------------------------------------------   public interface ViewResolver {          View resolveViewName(String viewName, Locale locale) throws Exception;      }  

    [java]  view plain  copy   public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {          private int order = Integer.MAX_VALUE;  // default: same as non-Ordered             public void setOrder(int order) {           this.order = order;       }          @Override       public int getOrder() {           return this.order;       }             @Override       public View resolveViewName(String viewName, Locale locale) throws BeansException {           ApplicationContext context = getApplicationContext();           if (!context.containsBean(viewName)) {               if (logger.isDebugEnabled()) {                   logger.debug("No matching bean found for view name '" + viewName + "'");               }               return null;           }                      if (!context.isTypeMatch(viewName, View.class)) {               if (logger.isDebugEnabled()) {                   logger.debug("Found matching bean for view name '" + viewName +                           "' - to be ignored since it does not implement View");               }               return null;           }           return context.getBean(viewName, View.class);       }      }       这样就得到了需要的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   // public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware           @Override       public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {           if (logger.isTraceEnabled()) {               logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +                   " and static attributes " + this.staticAttributes);           }              // 这里把所有的相关信息都收集到一个Map里 ------》           Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);           prepareResponse(request, response);              // 展现模型数据到视图的调用方法 -------->这是一个模板方法           renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);       }             protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,               HttpServletResponse response) {              @SuppressWarnings("unchecked")           Map<String, Object> pathVars = (this.exposePathVariables ?                   (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);              // Consolidate static and dynamic model attributes.           int size = this.staticAttributes.size();           size += (model != null ? model.size() : 0);           size += (pathVars != null ? pathVars.size() : 0);              Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);           mergedModel.putAll(this.staticAttributes);           if (pathVars != null) {               mergedModel.putAll(pathVars);           }           if (model != null) {               mergedModel.putAll(model);           }              // Expose RequestContext?           if (this.requestContextAttribute != null) {               mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));           }              return mergedModel;       }             protected abstract void renderMergedOutputModel(               Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;   [java]  view plain  copy   // ----------------------------------------------------   // public class InternalResourceView extends AbstractUrlBasedView           @Override       protected void renderMergedOutputModel(               Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {              // 1.对模型数据进行处理,把模型对象存放到ServletContext中           exposeModelAsRequestAttributes(model, request); // 1.--------------->              // Expose helpers as request attributes, if any.           // 这是一个模板方法,在JstlView中实现           exposeHelpers(request);  2.--------------->              // 获取InternalResource定义的内部资源路径           String dispatcherPath = prepareForRendering(request, response); // ----------->              // 把请求转发到前面获取的内部资源路径中去           RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);           if (rd == null) {               throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +                       "]: Check that the corresponding file exists within your web application archive!");           }              // If already included or response already committed, perform include, else forward.           if (useInclude(request, response)) {               response.setContentType(getContentType());               if (logger.isDebugEnabled()) {                   logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");               }               rd.include(request, response);           }              else {               // Note: The forwarded resource is supposed to determine the content type itself.               if (logger.isDebugEnabled()) {                   logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");               }               rd.forward(request, response);           }       }          protected void exposeHelpers(HttpServletRequest request) throws Exception {       }      // 1.-------------------------------------   // public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware          /* 这个方法把ModelAndView中的模型数据和其他请求数据都放到HttpServletRequest的属性中去,        这样就可以通过HttpServletRequest的属性得到和使用这些数据了。*/       protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {           for (Map.Entry<String, Object> entry : model.entrySet()) {               String modelName = entry.getKey();               Object modelValue = entry.getValue();               if (modelValue != null) {                   request.setAttribute(modelName, modelValue);                   if (logger.isDebugEnabled()) {                       logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +                               "] to request in view with name '" + getBeanName() + "'");                   }               }               else {                   request.removeAttribute(modelName);                   if (logger.isDebugEnabled()) {                       logger.debug("Removed model object '" + modelName +                               "' from request in view with name '" + getBeanName() + "'");                   }               }           }       }      // 2.-------------------------------------   // public class JstlView extends InternalResourceView          @Override       protected void exposeHelpers(HttpServletRequest request) throws Exception {           if (this.messageSource != null) {               JstlUtils.exposeLocalizationContext(request, this.messageSource);           }           else {               JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));           }       }        在这些处理的基础 上,实际的数据到页面的输出是由InternalResourceView来完成的,render完成资源的重定向处理。需要做的是,在得到实际视图的InternalResource路径以后,把请求转发到资源中去。如何得到资源的路径呢?在InternalResourceView中可以看到调用,如下代码。在这里可以看到,从request取得URL的路径,并对取得的路径进行了相应的处理。 [java]  view plain  copy   protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)           throws Exception {          String path = getUrl();       if (this.preventDispatchLoop) {           String uri = request.getRequestURI();           if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {               throw new ServletException("Circular view path [" + path + "]: would dispatch back " +                       "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +                       "(Hint: This may be the result of an unspecified view, due to default view name generation.)");           }       }       return path;   }       在得到URL路径之后,使用RequestDispatcher把请求转发到这个资源上,就完成了带JSTL的JSP页面的展现。 转载出处: http://blog.csdn.net/oChangWen/article/details/61616464 转载出处
    转载请注明原文地址: https://ju.6miu.com/read-36515.html

    最新回复(0)