Spring3.2 MVC 分析

jopen 10年前

Spring3.2 MVC 分析:

SpringMVC现在应该用得很广泛了,其配置清晰,灵活度,定制能力等都是很强的,相比Struts2也是胜过一筹,还是从源码来分析一下,SpringMVC为我们做了什么。

  • 先从配置文件开始,看web.xml,用过SpringMVC的同学应该都很熟悉:
<web-app>      <listener>   <listener-class>       org.springframework.web.context.ContextLoaderListener<!-- Spring根容器在这里被建立起来 -->   </listener-class>      </listener>      <context-param>   <param-name>contextConfigLocation</param-name>   <param-value>classpath*:configs/beans.xml</param-value>      </context-param>      <!-- SpringMVC子容器会在DispatherServlet初始化过程中被建立起来 -->      <servlet>          <servlet-name>example</servlet-name>          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>          <load-on-startup>1</load-on-startup>      </servlet>        <servlet-mapping>          <servlet-name>example</servlet-name>          <url-pattern>/*</url-pattern>      </servlet-mapping>    </web-app>

看看SpringMVC官方的请求处理流程图,DispacherServlet就是那个FrontController, 根据用户请求的url,匹配到我们自定义的控制器,然后进行业务处理,处理完成,将数据model返回给FrontController,然后它将数据+视图模版(如jsp, freemarker等)进行渲染,然后返回到用户浏览器:

那到底要做些什么工作,才能达到上面这样的流程呢,往下说。

看看DispatcherServlet怎么来的:

先从HttpServletBean类入手,人类都知道Servlet被Servlet容器创建时,会调用一次其init方法,我们就看看HttpServletBean的init方法,其主要工作就是将Servlet配置参数映射到我们的Servlet Bean里面,然后再初始化子类(initServletBean):

@Override  public final void init() throws ServletException {      // Set bean properties from init parameters.      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);      BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);      ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());      bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));      initBeanWrapper(bw);      bw.setPropertyValues(pvs, true);        // Let subclasses do whatever initialization they like.      initServletBean();      ...  }
接着看initServletBean方法,该方法由FrameworkServlet, 从其名称就知道,它时Spring Web框架的基础Servlet, Spring这样描述它的工作:
* Manages a WebApplicationContext instance per servlet. The servlet's configuration is determined by beans      in the servlet's namespace.  * Publishes events on request processing, whether or not a request is successfully handled.

它如何实现initServletBean, 它做的事很单一,就是初始化WebApplicationContext对象,initFrameworkServlet是个空方法,留给子类去实现:

protected final void initServletBean() throws ServletException {      ...        this.webApplicationContext = initWebApplicationContext();      initFrameworkServlet();      ...    }

看看FrameworkServlet怎么初始化WebApplicationContext的,其实类似于初始化IoC容器(如之前Spring Ioc文章讲XmlClasspathApplicationContext):

protected WebApplicationContext initWebApplicationContext() {                                                                   // 通过ServletContext对象获取到ContextLoaderListener建立的根容器,它到底怎么被建立起来的,后面再讲      WebApplicationContext rootContext =          WebApplicationContextUtils.getWebApplicationContext(getServletContext());      WebApplicationContext wac = null;        if (this.webApplicationContext != null) { //若通过构造参数传递了webApplicationContext          // A context instance was injected at construction time -> use it          wac = this.webApplicationContext;          if (wac instanceof ConfigurableWebApplicationContext) {              ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                           if (!cwac.isActive()) {                  // The context has not yet been refreshed -> provide services such as                  // setting the parent context, setting the application context id, etc                  if (cwac.getParent() == null) {                      // The context instance was injected without an explicit parent -> set                      // the root application context (if any; may be null) as the parent                      cwac.setParent(rootContext);                  }                  configureAndRefreshWebApplicationContext(cwac);              }          }      }      if (wac == null) {          // 如果配置了contextAttribute属性,通过其来获取WebApplicationContext对象          wac = findWebApplicationContext();      }      if (wac == null) {           // 这里创建FrameworkServlet的上下文实例,默认实现是XmlWebApplicationContext, 且以根上下文为父上下文          wac = createWebApplicationContext(rootContext);      }      //触发refresh事件, 会调用DispatcherServlet的onRefresh方法      if (!this.refreshEventReceived) {      }      //默认会将FrameworkServlet对象的上下文对象存到ServletContext中      if (this.publishContext) {          // Publish the context as a servlet context attribute.   String attrName = getServletContextAttributeName();   getServletContext().setAttribute(attrName, wac);      }      return wac;  }

我们可以看看createWebApplicationContext()怎么建立其XmlWebApplicationContext上下文对象的:

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {      Class<?> contextClass = getContextClass(); //没有配置,则默认XmlWebApplicationContext.class      ConfigurableWebApplicationContext wac =   (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);      wac.setEnvironment(getEnvironment());      wac.setParent(parent); //将根上下文作为父上下文      wac.setConfigLocation(getContextConfigLocation());//是否再xml中配置了contextConfigLocation属性      configureAndRefreshWebApplicationContext(wac); //如同之前文章将的Spring IoC就是初始化Ioc容器      return wac;  }
那XmlWebApplicationContext的初始化与普通容器如XmlClassPathApplicationContext有什么不同呢?其实现是很简洁的:
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {   public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";   public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";   public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";     @Override //这个方法就是再AbstractApplicationContext中的模版方法refresh中调用,和一般IoC容器初始化一样   protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);      beanDefinitionReader.setEnvironment(this.getEnvironment());    beanDefinitionReader.setResourceLoader(this);    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));      initBeanDefinitionReader(beanDefinitionReader);    loadBeanDefinitions(beanDefinitionReader);   }     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {   }     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {    String[] configLocations = getConfigLocations();    if (configLocations != null) {     for (String configLocation : configLocations) {      reader.loadBeanDefinitions(configLocation);     }    }   }     @Override   protected String[] getDefaultConfigLocations() {    if (getNamespace() != null) {      return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};//就是web.xml中配置的DispatcherServlet的servlet-name,对应我们就会有/WEB-INF/example-servlet.xml    }    else {     return new String[] {DEFAULT_CONFIG_LOCATION};    }   }    }

简单就分析完FrameworkServlet的上下文对象的构建过程,继续看DispatcherServlet怎么构建自己的,就从FrameworkServlet中initWebApplicationContext的onRefresh中,对应DispatcherServlet中的OnRefresh方法:

@Override  protected void onRefresh(ApplicationContext context) {      initStrategies(context);  }  protected void initStrategies(ApplicationContext context) {      initMultipartResolver(context); //初始化multipartResolver bean, 用于文件上传处理      initLocaleResolver(context);    //初始化localeResolver bean, 用于解析用户端Locale信息,默认实现AcceptHeaderLocaleResolver      initThemeResolver(context); //初始化themeResolver bean, 默认实现FixedThemeResolver      initHandlerMappings(context); //初始化handlerMappings集,将用户请求映射到我们定义的Controller中      initHandlerAdapters(context); //初始化handlerAdapters      initHandlerExceptionResolvers(context); //初始化异常处理解析器      initRequestToViewNameTranslator(context); //初始化请求到视图的翻译器      initViewResolvers(context); //初始化视图解析器,如jsp, freemarker等      initFlashMapManager(context); //初始化FlashMap管理器,实现再不同请求中共享参数,如重定向。  }

上面这些组件都有默认实现,spring将默认实现放在和DispatcherServlet.java同包下的DispatcherServlet.properties中:

# Default implementation classes for DispatcherServlet's strategy interfaces.  # Used as fallback when no matching beans are found in the DispatcherServlet context.  # Not meant to be customized by application developers.    org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver    org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver    org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\   org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\   org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\   org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\   org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver    org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator    org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver    org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
重点看看handlerMappdings怎么建立起来的:
private void initHandlerMappings(ApplicationContext context) {    this.handlerMappings = null;      //默认会从父上下文递归地去获取HandlerMapping bean      if (this.detectAllHandlerMappings) {          // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.          Map<String, HandlerMapping> matchingBeans =              BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);              if (!matchingBeans.isEmpty()) {                  this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());                  // We keep HandlerMappings in sorted order.                  OrderComparator.sort(this.handlerMappings);              }      }else { //仅从当前上下文获取HandlerMapping bean          HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);          this.handlerMappings = Collections.singletonList(hm);        }      if (this.handlerMappings == null) { //若没有配置,根据上面的属性文件提供默认实现          this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);      }  }

看看HandlerMapping接口的定义,十分简洁,就一个getHandler接口:

public interface HandlerMapping {     ...     //根据当前request对象,获取HandlerExecutionChain对象,其包括一个handler对象(我们定义的Controller)和一些定义的拦截器     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  }
其继承树:

HandlerExecutionChain确实保存了handler和interceptors:

public class HandlerExecutionChain {        private final Object handler;        private HandlerInterceptor[] interceptors;        private List<HandlerInterceptor> interceptorList;        private int interceptorIndex = -1;      ...  }

看一个HandlerMapping的简单实现SimpleUrlHandlerMapping, 我们需要配置urlMap属性,通过registerHandlers方法将urlMap的属性注册到父类的handlerMap中,其保存了url请求与handler bean的对应关系:

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {      private final Map<String, Object> urlMap = new HashMap<String, Object>();        protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {          if (urlMap.isEmpty()) {              logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");          } else {              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); //由父类实现              }                                                                                                    }  }
继续看AbstractUrlHandlerMapping怎么实现registerHandler()方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {      Object resolvedHandler = handler;      // Eagerly resolve handler if referencing singleton via name.      if (!this.lazyInitHandlers && handler instanceof String) {//若为非懒加载且是单例bean,则从容器中获取          String handlerName = (String) handler;          if (getApplicationContext().isSingleton(handlerName)) {              resolvedHandler = getApplicationContext().getBean(handlerName);          }      }        Object mappedHandler = this.handlerMap.get(urlPath);      if (mappedHandler != null) { //一个url只能有一个对应的handler          if (mappedHandler != resolvedHandler) {              throw new IllegalStateException(                  "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +                  "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");          }      } else {          if (urlPath.equals("/")) {              setRootHandler(resolvedHandler);          } else if (urlPath.equals("/*")) {              setDefaultHandler(resolvedHandler);          } else {              this.handlerMap.put(urlPath, resolvedHandler);          }      }  }
这上面就是HandlerMapping bean怎么被注册起来的,下面是HandlerMapping Bean怎么完成请求的映射处理的。

看看AbstractHandlerMapping的getHandler(), 根据当前请求来获取对应的HandlerExecuteChain:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {      Object handler = getHandlerInternal(request); // 获取handler对象      if (handler == null) {          handler = getDefaultHandler(); //默认Handler对象,就是上面注册的"/"的handler对象      }      if (handler == null) {          return null;      }      // Bean name or resolved handler?      if (handler instanceof String) { //如果是bean name就从容器中获取          String handlerName = (String) handler;          handler = getApplicationContext().getBean(handlerName);      }      return getHandlerExecutionChain(handler, request); //获取handlerExecutionChain  }
接着看getHandlerInternal怎么获得handler对象(AbstractUrlHanlderMapping):
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //提取请求url      Object handler = lookupHandler(lookupPath, request); //根据url查找handler      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);          }      }      return handler;  }

由lookupHandler(url,request)查找handler对象:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {      // Direct match?      Object handler = this.handlerMap.get(urlPath); //精确匹配获取handler      if (handler != null) {          // Bean name or resolved handler?          if (handler instanceof String) {               String handlerName = (String) handler;              handler = getApplicationContext().getBean(handlerName);          }          validateHandler(handler, request); //验证handler,由子类实现          return buildPathExposingHandler(handler, urlPath, urlPath, null);      }      //正则匹配获取handler      List<String> matchingPatterns = new ArrayList<String>();      for (String registeredPattern : this.handlerMap.keySet()) {          if (getPathMatcher().match(registeredPattern, urlPath)) {              matchingPatterns.add(registeredPattern);          }      }      String bestPatternMatch = null;      Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);      if (!matchingPatterns.isEmpty()) {          Collections.sort(matchingPatterns, patternComparator);          bestPatternMatch = matchingPatterns.get(0);      }      if (bestPatternMatch != null) {          handler = this.handlerMap.get(bestPatternMatch);          // 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);              }          }          return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);      }      // No handler found...      return null;  }
上面就明白了根据请求获取handler对象的过程, 下面讲讲请求是怎么被DispatcherServlet分发处理的.

DispatcherServlet分发处理主要方法为doServlce方法开始:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {      // 如果request请求是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));            }         }      }        //设置一些涉及MVC框架的属性,以方便handler或view等引用.      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属性,该属性是基于Session实现,主要是在不同request请求间传递参数,典型的就是Redirect这种情况      FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);      if (inputFlashMap != null) {          request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));      }      request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());      request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);        try {          doDispatch(request, response); //真正实现将请求进行转发      } finally {          if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {              return;          }          // Restore the original attribute snapshot, in case of an include.          if (attributesSnapshot != null) {              restoreAttributesAfterInclude(request, attributesSnapshot);          }      }  }

看doDispatch():

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {      HttpServletRequest processedRequest = request;      HandlerExecutionChain mappedHandler = null;      boolean multipartRequestParsed = false;        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);      ModelAndView mv = null;      Exception dispatchException = null;      processedRequest = checkMultipart(request);//检查request是否Multipart, 即是否上传文件      multipartRequestParsed = processedRequest != request;        //获取当前请求对应的HandlerExecutionChain对象      mappedHandler = getHandler(processedRequest, false);      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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {            return;         }      }      if (!mappedHandler.applyPreHandle(processedRequest, response)) {//前置处理,调用chain的拦截器预处理方法preHandle            return;      }      ...      //调用handler处理方法,最终会调用Controller的handleRequest方法,并返回ModelAndView对象      mv = ha.handle(processedRequest, response, mappedHandler.getHandler());      ...      applyDefaultViewName(request, mv);//解析视图名称,就是我们配置的prefix + uri + suffix      mappedHandler.applyPostHandle(processedRequest, response, mv);//后置处理,调用chain的拦截器postHandle方法      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); //进行视图渲染      ...  }

接着看processDispatchResult()方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,     HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {      boolean errorView = false;      if (exception != null) { //如果有异常          if (exception instanceof ModelAndViewDefiningException) {              logger.debug("ModelAndViewDefiningException encountered", exception);              mv = ((ModelAndViewDefiningException) exception).getModelAndView();          } else {              Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);              mv = processHandlerException(request, response, handler, exception);              errorView = (mv != null);          }      }        // Did the handler return a view to render?      if (mv != null && !mv.wasCleared()) {          render(mv, request, response); //渲染视图的最后一个步骤    if (errorView) {              WebUtils.clearErrorRequestAttributes(request);          }      }else {          if (logger.isDebugEnabled()) {       logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +        "': assuming HandlerAdapter completed request handling");          }      }        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {          // Concurrent handling started during a forward          return;      }        if (mappedHandler != null) {          mappedHandler.triggerAfterCompletion(request, response, null);      }  }
看看render怎么工作的:
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()) { // 若ModalAndView中保存了view的引用          //根据我们配置的ViewReslover bean,解析视图名称          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 {  //ModalAndView中保存了实际的view对象          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() + "'");          }      }      view.render(mv.getModelInternal(), request, response); //渲染视图  }
上面讲述了DispatcherServlet的请求分发过程及视图渲染大致流程。接下来得说说视图渲染得话题,默认DispatcherServlet的默认视图实是 InternalResourceViewResolver, 也就是jsp视图,看看jsp视图是怎么被呈现的。

先看AbstractView的render方法:

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {      Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);//合并动态和静态属性      prepareResponse(request, response);      renderMergedOutputModel(mergedModel, request, response); //用模型渲染视图  }

看看InternalResourceView的renderMergeOutputModel怎么实现的:

protected void renderMergedOutputModel(     Map<String, Object> model, HttpServletRequest request, HttpServletResponse response){        // Determine which request handle to expose to the RequestDispatcher.      HttpServletRequest requestToExpose = getRequestToExpose(request);        //将model放入到request属性中      exposeModelAsRequestAttributes(model, requestToExpose);        // Expose helpers as request attributes, if any.      exposeHelpers(requestToExpose);        //获取request对象dispath的path      String dispatcherPath = prepareForRendering(requestToExpose, response);        //获取RequestDispatcher对象      RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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(requestToExpose, response)) {          response.setContentType(getContentType());          rd.include(requestToExpose, response);      }else {          // Note: The forwarded resource is supposed to determine the content type itself.          exposeForwardRequestAttributes(requestToExpose);          rd.forward(requestToExpose, response);      }  }

这上面就讲述了MVC中视图的工作。

  • 关于Spring根容器的建立, 之前说了,Spring会建立根上下文,再建立MVC上下文,我们简略说下根上下文的建立,也就是web.xml的ContextLoaderListener配置:
<listener>      <listener-class>          org.springframework.web.context.ContextLoaderListener      </listener-class>  </listener>
看看ContextLoaderListener, 它继承自ContextLoader, 主要初始化工作还是由ContextLoader来完成
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {     ...  }
看看ContextLoader的initWebApplicationContext方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {      if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {   ...      }      long startTime = System.currentTimeMillis();        // Store context in local instance variable, to guarantee that      // it is available on ServletContext shutdown.      if (this.context == null) {          this.context = createWebApplicationContext(servletContext);//创建根上下文      }      if (this.context instanceof ConfigurableWebApplicationContext) {          ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;          if (!cwac.isActive()) {              // The context has not yet been refreshed -> provide services such as              // setting the parent context, setting the application context id, etc              if (cwac.getParent() == null) {              // The context instance was injected without an explicit parent ->              // determine parent for root web application context, if any.              ApplicationContext parent = loadParentContext(servletContext);                  cwac.setParent(parent);              }              configureAndRefreshWebApplicationContext(cwac, servletContext);//构建上下文,类似IoC容器构建          }      }                                                                                                           //将创建的根上下文保存到ServletContext, 所以再MVC上下文中可以获取到      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);      ClassLoader ccl = Thread.currentThread().getContextClassLoader();      if (ccl == ContextLoader.class.getClassLoader()) {          currentContext = this.context;      } else if (ccl != null) {          currentContextPerThread.put(ccl, this.context);      }      return this.context;  }
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {      Class<?> contextClass = determineContextClass(sc);//默认实现为XmlWebApplicationContext      if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {          throw new ApplicationContextException("Custom context class [" + contextClass.getName() +              "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");      }      return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  }

这就简略说了Spring根上下文的建立过程。

来自:http://my.oschina.net/indestiny/blog/197061