程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Spring源代碼解析(四):Spring MVC

Spring源代碼解析(四):Spring MVC

編輯:關於JAVA

下面我們對Spring MVC框架代碼進行分析,對於webApplicationContext的相關分析可 以參見以前的文檔,我們這裡著重分析Spring Web MVC框架的實現.我們從分析 DispatcherServlet入手:

代碼

//這裡是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各 個Spring MVC主要元素的初始化
protected void initFrameworkServlet() throws ServletException, BeansException {
   initMultipartResolver();
   initLocaleResolver();
   initThemeResolver();
   initHandlerMappings();
   initHandlerAdapters();
   initHandlerExceptionResolvers();
   initRequestToViewNameTranslator();
   initViewResolvers();
}

看到注解我們知道,這是DispatcherSerlvet的初始化過程,它是在 WebApplicationContext已經存在的情況下進行的,也就意味著在初始化它的時候,IOC容 器應該已經工作了,這也是我們在web.xml中配置Spring的時候,需要把 DispatcherServlet的 load-on-startup的屬性配置為2的原因。

對於具體的初始化過程,很容易理解,我們拿initHandlerMappings()來看看:

代碼

private void initHandlerMappings() throws BeansException {
   if (this.detectAllHandlerMappings) {
     // 這裡找到所有在上下文中定義的HandlerMapping,同時把他們排序
     // 因為在同一個上下文中可以有不止一個handlerMapping,所以我們把他們 都載入到一個鏈裡進行維護和管理
     Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors (
         getWebApplicationContext(), HandlerMapping.class, true, false);
     if (!matchingBeans.isEmpty()) {
       this.handlerMappings = new ArrayList(matchingBeans.values ());
       // 這裡通過order屬性來對handlerMapping來在list中排序
       Collections.sort(this.handlerMappings, new OrderComparator ());
     }
   }
   else {
     try {
       Object hm = getWebApplicationContext().getBean (HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
       this.handlerMappings = Collections.singletonList(hm);
     }
     catch (NoSuchBeanDefinitionException ex) {
       // Ignore, we'll add a default HandlerMapping later.
     }
   }

   //如果在上下文中沒有定義的話,那麼我們使用默認的 BeanNameUrlHandlerMapping
   if (this.handlerMappings == null) {
     this.handlerMappings = getDefaultStrategies (HandlerMapping.class);
   ........
   }
}

怎樣獲得上下文環境,可以參見我們前面的對IOC容器在web環境中加載的分析。 DispatcherServlet把定義了的所有HandlerMapping都加載了放在一個List裡待以後進行 使用,這個鏈的每一個元素都是一個handlerMapping的配置,而一般每一個 handlerMapping可以持有一系列從URL請求到 Spring Controller的映射,比如 SimpleUrl

HandlerMaaping中就定義了一個map來持有這一系列的映射關系。

DisptcherServlet通過HandlerMapping使得Web應用程序確定一個執行路徑,就像我們 在HanderMapping中看到的那樣,HandlerMapping只是一個借口:

代碼

public interface HandlerMapping {
  public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
           Conventions.getQualifiedAttributeName (HandlerMapping.class, "pathWithinHandlerMapping");
    //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用, 這個執行鏈裡面維護handler和攔截器
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

他的具體實現只需要實現一個接口方法,而這個接口方法返回的是一個 HandlerExecutionChain,實際上就是一個執行鏈,就像在Command模式描述的那樣,這個 類很簡單,就是一個持有一個Interceptor鏈和一個Controller:

代碼

public class HandlerExecutionChain {

   private Object handler;

   private HandlerInterceptor[] interceptors;

   ........
}

而這些Handler和Interceptor需要我們定義HandlerMapping的時候配置好,比如對具 體的 SimpleURLHandlerMapping,他要做的就是根據URL映射的方式注冊Handler和 Interceptor,自己維護一個放映映射的handlerMap,當需要匹配Http請求的時候需要使 用這個表裡的信息來得到執行鏈。這個注冊的過程在IOC容器初始化 SimpleUrlHandlerMapping的時候就被完成了,這樣以後的解析才可以用到map裡的映射信 息,這裡的信息和bean文件的信息是等價的,下面是具體的注冊過程:

代碼

protected void registerHandlers(Map urlMap) throws BeansException {
   if (urlMap.isEmpty()) {
     logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
   }
   else {
     //這裡迭代在SimpleUrlHandlerMapping中定義的所有映射元素
     Iterator it = urlMap.keySet().iterator();
     while (it.hasNext()) {
       //這裡取得配置的url  
       String url = (String) it.next();
       //這裡根據url在bean定義中取得對應的handler
       Object handler = urlMap.get(url);
       // Prepend with slash if not already present.
       if (!url.startsWith("/")) {
         url = "/" + url;
       }
       //這裡調用AbstractHandlerMapping中的注冊過程
       registerHandler(url, handler);
     }
   }
}

在AbstractMappingHandler中的注冊代碼:

代碼

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關系
   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
   ........
   }

   //如果是直接用bean名做映射那就直接從容器中取handler
   if (!this.lazyInitHandlers && handler instanceof String) {
     String handlerName = (String) handler;
     if (getApplicationContext().isSingleton(handlerName)) {
       handler = getApplicationContext().getBean(handlerName);
     }
   }
   //或者使用默認的handler.
   if (urlPath.equals("/*")) {
     setDefaultHandler(handler);
   }
   else {
  //把url和handler的對應關系放到handlerMap中去
     this.handlerMap.put(urlPath, handler);
     ........
   }
}

handlerMap是持有的一個HashMap,裡面就保存了具體的映射信息:

代碼

private final Map handlerMap = new HashMap();

而SimpleUrlHandlerMapping對接口HandlerMapping的實現是這樣的,這個getHandler 根據在初始化的時候就得到的映射表來生成DispatcherServlet需要的執行鏈

代碼

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   //這裡根據request中的參數得到其對應的handler,具體處理在 AbstractUrlHandlerMapping中
   Object handler = getHandlerInternal(request);
   //如果找不到對應的,就使用缺省的handler
   if (handler == null) {
     handler = this.defaultHandler;
   }
   //如果缺省的也沒有,那就沒辦法了
   if (handler == null) {
     return null;
   }
   // 如果handler不是一個具體的handler,那我們還要到上下文中取
   if (handler instanceof String) {
     String handlerName = (String) handler;
     handler = getApplicationContext().getBean(handlerName);
   }
   //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔 截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截 器組。
   return new HandlerExecutionChain(handler, this.adaptedInterceptors);
}

我們看看具體的handler查找過程:

代碼

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   //這裡的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。
   String lookupPath = this.urlPathHelper.getLookupPathForRequest (request);
   .......//下面是根據請求信息的查找
   return lookupHandler(lookupPath, request);
}

protected Object lookupHandler(String urlPath, HttpServletRequest request) {
   // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
   Object handler = this.handlerMap.get(urlPath);
   if (handler == null) {
     // 這裡使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher 類來完成匹配處理。
     String bestPathMatch = null;
     for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext ();) {
       String registeredPath = (String) it.next();
       if (this.pathMatcher.match(registeredPath, urlPath) &&
               (bestPathMatch == null || bestPathMatch.length () <= registeredPath.length())) {
         //這裡根據匹配路徑找到最象的一個  
         handler = this.handlerMap.get(registeredPath);
         bestPathMatch = registeredPath;
       }
     }

     if (handler != null) {
       exposePathWithinMapping (this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
     }
   }
   else {
     exposePathWithinMapping(urlPath, request);
   }
   //
   return handler;
}

我們可以看到,總是在handlerMap這個HashMap中找,當然如果直接找到最好,如果找 不到,就看看是不是能通過Match Pattern的模式找,我們一定還記得在配置 HnaderMapping的時候是可以通過ANT語法進行配置的,其中的處理就在這裡。

這樣可以清楚地看到整個HandlerMapping的初始化過程 - 同時,我們也看到了一個具 體的handler映射是怎樣被存儲和查找的 - 這裡生成一個ExecutionChain來儲存我們找到 的handler和在定義bean的時候定義的Interceptors.

讓我們回到DispatcherServlet,初始化完成以後,實際的對web請求是在doService() 方法中處理的,我們知道DispatcherServlet只是一個普通的Servlet:

代碼

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   .......
   //這裡把屬性信息進行保存
   Map attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
     logger.debug("Taking snapshot of request attributes before include");
     attributesSnapshot = new HashMap();
     Enumeration attrNames = request.getAttributeNames();
     while (attrNames.hasMoreElements()) {
       String attrName = (String) attrNames.nextElement();
       if (this.cleanupAfterInclude || attrName.startsWith (DispatcherServlet.class.getName())) {
         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());

   try {
     //這裡使實際的處理入口
     doDispatch(request, response);
   }
   finally {
     // Restore the original attribute snapshot, in case of an include.
     if (attributesSnapshot != null) {
       restoreAttributesAfterInclude(request, attributesSnapshot);
     }
   }
}

我們看到,對於請求的處理實際上是讓doDispatch()來完成的 - 這個方法很長,但是 過程很簡單明了:

代碼

protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   //這是從handlerMapping中得到的執行鏈
   HandlerExecutionChain mappedHandler = null;
   int interceptorIndex = -1;

   ........
   try {
     //我們熟悉的ModelAndView開始出現了。
     ModelAndView mv = null;
     try {
       processedRequest = checkMultipart(request);

       // 這是我們得到handler的過程  
       mappedHandler = getHandler(processedRequest, false);
       if (mappedHandler == null || mappedHandler.getHandler() == null) {
         noHandlerFound(processedRequest, response);
         return;
       }

       // 這裡取出執行鏈中的Interceptor進行前處理
       if (mappedHandler.getInterceptors() != null) {
         for (int i = 0; i < mappedHandler.getInterceptors ().length; i++) {
           HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
           if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
             return;
           }
           interceptorIndex = i;
         }
       }

       //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性 :是不是按Spring的要求編寫的。
       HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler ());
       mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

       // 這裡取出執行鏈中的Interceptor進行後處理
       if (mappedHandler.getInterceptors() != null) {
         for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
           HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
           interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
         }
       }
     }

     ........

     // Did the handler return a view to render?
     //這裡對視圖生成進行處理
     if (mv != null && !mv.wasCleared()) {
       render(mv, processedRequest, response);
     }
     .......
}

我們很清楚的看到和MVC框架緊密相關的代碼,比如如何得到和http請求相對應的執行 鏈,怎樣執行執行鏈和怎樣把模型數據展現到視圖中去。

先看怎樣取得Command對象,對我們來說就是Handler - 下面是getHandler的代碼:

代碼

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
  //在ServletContext取得執行鏈 - 實際上第一次得到它的時候,我們把它放在 ServletContext進行了緩存。
  HandlerExecutionChain handler =
       (HandlerExecutionChain) request.getAttribute (HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
   if (handler != null) {
     if (!cache) {
       request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
     }
     return handler;
   }
   //這裡的迭代器迭代的時在initHandlerMapping中載入的上下文所有的 HandlerMapping
   Iterator it = this.handlerMappings.iterator();
   while (it.hasNext()) {
     HandlerMapping hm = (HandlerMapping) it.next();
     .......
     //這裡是實際取得handler的過程,在每個HandlerMapping中建立的映射表進 行檢索得到請求對應的handler
     handler = hm.getHandler(request);

     //然後把handler存到ServletContext中去進行緩存
     if (handler != null) {
       if (cache) {
         request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
       }
       return handler;
     }
   }
   return null;
}

如果在ServletContext中可以取得handler則直接返回,實際上這個handler是緩沖了 上次處理的結果 - 總要有第一次把這個handler放到ServletContext中去:

如果在ServletContext中找不到handler,那就通過持有的handlerMapping生成一個, 我們看到它會迭代當前持有的所有的 handlerMapping,因為可以定義不止一個,他們在定 義的時候也可以指定順序,直到找到第一個,然後返回。先找到一個 handlerMapping,然 後通過這個handlerMapping返回一個執行鏈,裡面包含了最終的Handler和我們定義的一 連串的 Interceptor。具體的我們可以參考上面的SimpleUrlHandlerMapping的代碼分析 知道getHandler是怎樣得到一個 HandlerExecutionChain的。

得到HandlerExecutionChain以後,我們通過HandlerAdapter對這個Handler的合法性 進行判斷:

代碼

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   Iterator it = this.handlerAdapters.iterator();
   while (it.hasNext()) {
     //同樣對持有的所有adapter進行匹配
     HandlerAdapter ha = (HandlerAdapter) it.next();
     if (ha.supports(handler)) {
       return ha;
     }
   }
   ........
}

通過判斷,我們知道這個handler是不是一個Controller接口的實現,比如對於具體的 HandlerAdapter - SimpleControllerHandlerAdapter:

代碼

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

   public boolean supports(Object handler) {
     return (handler instanceof Controller);
   }
   .......
}

簡單的判斷一下handler是不是實現了Controller接口。這也體現了一種對配置文件進 行驗證的機制。

讓我們再回到DispatcherServlet看到代碼:

代碼

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

這個就是對handle的具體調用!相當於Command模式裡的Command.execute();理所當然 的返回一個ModelAndView,下面就是一個對View進行處理的過程:

代碼

if (mv != null && !mv.wasCleared()) {
   render(mv, processedRequest, response);
}

調用的是render方法:

代碼

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
     throws Exception {response.setLocale(locale);

   View view = null;
   //這裡把默認的視圖放到ModelAndView中去。
   if (!mv.hasView()) {
     mv.setViewName(getDefaultViewName(request));
   }

   if (mv.isReference()) {
     // 這裡對視圖名字進行解析
     view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
   .......
   }
   else {
     // 有可能在ModelAndView裡已經直接包含了View對象,那我們就直接使用。
     view = mv.getView();
   ........
   }

   //得到具體的View對象以後,我們用它來生成視圖。
   view.render(mv.getModelInternal(), request, response);
}

從整個過程我們看到先在ModelAndView中尋找視圖的邏輯名,如果找不到那就使用缺 省的視圖,如果能夠找到視圖的名字,那就對他進行解析得到實際的需要使用的視圖對象 。還有一種可能就是在ModelAndView中已經包含了實際的視圖對象,這個視圖對象是可以 直接使用的。

不管怎樣,得到一個視圖對象以後,通過調用視圖對象的render來完成數據的顯示過 程,我們可以看看具體的JstlView是怎樣實現的,我們在JstlView的抽象父類 AbstractView中找到render方法:

代碼

public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
   ......
   // 這裡把所有的相關信息都收集到一個Map裡
   Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
   mergedModel.putAll(this.staticAttributes);
   if (model != null) {
     mergedModel.putAll(model);
   }

   // Expose RequestContext?
   if (this.requestContextAttribute != null) {
     mergedModel.put(this.requestContextAttribute, createRequestContext (request, mergedModel));
   }
   //這是實際的展現模型數據到視圖的調用。
   renderMergedOutputModel(mergedModel, request, response);
}

注解寫的很清楚了,先把所有的數據模型進行整合放到一個Map - mergedModel裡,然 後調用renderMergedOutputModel();這個renderMergedOutputModel是一個模板方法,他 的實現在InternalResourceView也就是JstlView的父類:

Java代碼

protected void renderMergedOutputModel(
       Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
     // Expose the model object as request attributes.
     exposeModelAsRequestAttributes(model, request);
     // Expose helpers as request attributes, if any.
     exposeHelpers(request);
     // 這裡得到InternalResource定義的內部資源路徑。
     String dispatcherPath = prepareForRendering(request, response);
     //這裡把請求轉發到前面得到的內部資源路徑中去。
     RequestDispatcher rd = request.getRequestDispatcher (dispatcherPath);
     if (rd == null) {
       throw new ServletException(
           "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
     }
     .......
   }

首先對模型數據進行處理,exposeModelAsRequestAttributes是在AbstractView中實 現的,這個方法把 ModelAndView中的模型數據和其他request數據統統放到 ServletContext當中去,這樣整個模型數據就通過 ServletContext暴露並得到共享使用 了:

Java代碼

protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
     Iterator it = model.entrySet().iterator();
     while (it.hasNext()) {
       Map.Entry entry = (Map.Entry) it.next();
       ..........
       String modelName = (String) entry.getKey();
       Object modelValue = entry.getValue();
       if (modelValue != null) {
         request.setAttribute(modelName, modelValue);
       ...........
       }
       else {
         request.removeAttribute(modelName);
         .......
       }
     }
   }

讓我們回到數據處理部分的exposeHelper();這是一個模板方法,其實現在JstlView中 實現:

Java代碼

public class JstlView extends InternalResourceView {
   private MessageSource jstlAwareMessageSource;
   protected void initApplicationContext() {
     super.initApplicationContext();
     this.jstlAwareMessageSource =
         JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
   }
   protected void exposeHelpers(HttpServletRequest request) throws Exception {
     JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
   }
}

在JstlUtils中包含了對於其他而言jstl特殊的數據處理和設置。

過程是不是很長?我們現在在哪裡了?呵呵,我們剛剛完成的事MVC中View的render, 對於InternalResourceView的render 過程比較簡單只是完成一個資源的重定向處理。需 要做的就是得到實際view的internalResource路徑,然後轉發到那個資源中去。怎樣得到 資源的路徑呢通過調用:

Java代碼

protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
       throws Exception {
     return getUrl();
   }

那這個url在哪裡生成呢?我們在View相關的代碼中沒有找到,實際上,他在 ViewRosolve的時候就生成了,在UrlBasedViewResolver中:

Java代碼

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
     AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
     view.setUrl(getPrefix() + viewName + getSuffix());
     String contentType = getContentType();
     if (contentType != null) {
       view.setContentType(contentType);
     }
     view.setRequestContextAttribute(getRequestContextAttribute());
     view.setAttributesMap(getAttributesMap());
     return view;
   }

這裡是生成View的地方,自然也把生成的url和其他一些和view相關的屬性也配置好了 。

那這個ViewResolve是什麼時候被調用的呢?哈哈,我們這樣又要回到 DispatcherServlet中去看看究竟,在DispatcherServlet中:

Java代碼

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
       throws Exception {
     ........
     View view = null;
     // 這裡設置視圖名為默認的名字
     if (!mv.hasView()) {
       mv.setViewName(getDefaultViewName(request));
     }
     if (mv.isReference()) {
       //這裡對視圖名進行解析,在解析的過程中根據需要生成實際需要的視 圖對象。
       view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
      ..........
     }
    ......
  }

下面是對視圖名進行解析的具體過程:

Java代碼

protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
       throws Exception {
     //我們有可能不止一個視圖解析器
     for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
       ViewResolver viewResolver = (ViewResolver) it.next();
       //這裡是視圖解析器進行解析並生成視圖的過程。
       View view = viewResolver.resolveViewName(viewName, locale);
       if (view != null) {
         return view;
       }
     }
     return null;
   }

這裡調用具體的ViewResolver對視圖的名字進行解析 - 除了單純的解析之外,它還根 據我們的要求生成了我們實際需要的視圖對象。具體的viewResolver在bean定義文件中進 行定義同時在 initViewResolver()方法中被初始化到viewResolver變量中,我們看看具 體的 InternalResourceViewResolver是怎樣對視圖名進行處理的並生成V視圖對象的:對 resolveViewName的調用模板在 AbstractCachingViewResolver中,

Java代碼

public View resolveViewName(String viewName, Locale locale) throws Exception {
     //如果沒有打開緩存設置,那創建需要的視圖
     if (!isCache()) {
       logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
       return createView(viewName, locale);
     }
     else {
       Object cacheKey = getCacheKey(viewName, locale);
       // No synchronization, as we can live with occasional double caching.
       synchronized (this.viewCache) {
         //這裡查找緩存裡的視圖對象
         View view = (View) this.viewCache.get(cacheKey);
         if (view == null) {
           //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去
           view = createView(viewName, locale);
           this.viewCache.put(cacheKey, view);
         ........
         }
         return view;
       }
     }
   }

關於這些createView(),loadView(),buildView()的關系,我們看看Eclipse裡的call hiearchy

然後我們回到view.render中完成數據的最終對httpResponse的寫入,比如在 AbstractExcelView中的實現:

Java代碼

protected final void renderMergedOutputModel(
       Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
     .........
     // response.setContentLength(workbook.getBytes().length);
     response.setContentType(getContentType());
     ServletOutputStream out = response.getOutputStream();
     workbook.write(out);
     out.flush();
   }

這樣就和我們前面的分析一致起來了:DispatcherServlet在解析視圖名的時候就根據 要求生成了視圖對象,包括在InternalResourceView中需要使用的url和其他各種和HTTP response相關的屬性都會寫保持在生成的視圖對象中,然後就直接調用視圖對象的render 來完成數據的展示。

這就是整個Spring Web MVC框架的大致流程,整個MVC流程由DispatcherServlet來控 制。MVC的關鍵過程包括:

配置到handler的映射關系和怎樣根據請求參數得到對應的handler,在Spring中,這是 由handlerMapping通過執行鏈來完成的,而具體的映射關系我們在bean定義文件中定義並 在HandlerMapping載入上下文的時候就被配置好了。然後 DispatcherServlet調用 HandlerMapping來得到對應的執行鏈,最後通過視圖來展現模型數據,但我們要注意的是 視圖對象是在解析視圖名的時候生成配置好的。這些作為核心類的 HanderMapping,ViewResolver,View,Handler的緊密協作實現了MVC的功能。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved