国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Spring源代碼解析(四):Spring MVC

下面我們對Spring MVC框架代碼進行分析,對于webApplicationContext的相關(guān)分析可以參見以前的文檔,我們這里著重分析Spring Web MVC框架的實現(xiàn).我們從分析DispatcherServlet入手:

  代碼

//這里是對DispatcherServlet的初始化方法,根據(jù)名字我們很方面的看到對各個Spring MVC主要元素的初始化  

protected void initFrameworkServlet() throws ServletException, BeansException {  

  initMultipartResolver();  

  initLocaleResolver();  

  initThemeResolver();  

  initHandlerMappings();  

  initHandlerAdapters();  

  initHandlerExceptionResolvers();  

  initRequestToViewNameTranslator();  

  initViewResolvers();  

} 

  看到注解我們知道,這是DispatcherSerlvet的初始化過程,它是在WebApplicationContext已經(jīng)存在的情況下進行的,也就意味著在初始化它的時候,IOC容器應(yīng)該已經(jīng)工作了,這也是我們在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.  

    }  

  }  

 

  //如果在上下文中沒有定義的話,那么我們使用默認(rèn)的BeanNameUrlHandlerMapping  

  if (this.handlerMappings == null) {  

    this.handlerMappings = getDefaultStrategies(HandlerMapping.class);  

  ........  

  }  

} 

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

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

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

  代碼

public interface HandlerMapping {  

 public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =  

          Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");  

   //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執(zhí)行鏈里面維護handler和攔截器  

  HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  

}  

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

  代碼

public class HandlerExecutionChain {  

 

  private Object handler;  

 

  private HandlerInterceptor[] interceptors;  

   

  ........  

}  

而這些Handler和Interceptor需要我們定義HandlerMapping的時候配置好,比如對具體的 SimpleURLHandlerMapping,他要做的就是根據(jù)URL映射的方式注冊Handler和Interceptor,自己維護一個放映映射的handlerMap,當(dāng)需要匹配Http請求的時候需要使用這個表里的信息來得到執(zhí)行鏈。這個注冊的過程在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();  

      //這里根據(jù)url在bean定義中取得對應(yīng)的handler  

      Object handler = urlMap.get(url);  

      // Prepend with slash if not already present.  

      if (!url.startsWith("/")) {  

        url = "/" + url;  

      }  

      //這里調(diào)用AbstractHandlerMapping中的注冊過程  

      registerHandler(url, handler);  

    }  

  }  

}

在AbstractMappingHandler中的注冊代碼:

  代碼

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {  

  //試圖從handlerMap中取handler,看看是否已經(jīng)存在同樣的Url映射關(guān)系  

  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);  

    }  

  }  

  //或者使用默認(rèn)的handler.  

  if (urlPath.equals("/*")) {  

    setDefaultHandler(handler);  

  }  

  else {  

  //把url和handler的對應(yīng)關(guān)系放到handlerMap中去  

    this.handlerMap.put(urlPath, handler);  

    ........  

  }  

} 

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

  代碼

  private final Map handlerMap = new HashMap();  

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

代碼

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  

  //這里根據(jù)request中的參數(shù)得到其對應(yīng)的handler,具體處理在AbstractUrlHandlerMapping中  

  Object handler = getHandlerInternal(request);  

  //如果找不到對應(yīng)的,就使用缺省的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傳進來的參數(shù)進行分析,得到具體的路徑信息。  

  String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  

  .......//下面是根據(jù)請求信息的查找  

  return lookupHandler(lookupPath, request);  

}  

 

protected Object lookupHandler(String urlPath, HttpServletRequest request) {  

  // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好?! ?/p>

  Object handler = this.handlerMap.get(urlPath);  

  if (handler == null) {  

    // 這里使用模式來對map中的所有handler進行匹配,調(diào)用了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())) {  

        //這里根據(jù)匹配路徑找到最象的一個  

        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中找,當(dāng)然如果直接找到最好,如果找不到,就看看是不是能通過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中得到的執(zhí)行鏈  

  HandlerExecutionChain mappedHandler = null;  

  int interceptorIndex = -1;  

   

  ........  

  try {  

    //我們熟悉的ModelAndView開始出現(xiàn)了?! ?/p>

    ModelAndView mv = null;  

    try {  

      processedRequest = checkMultipart(request);  

 

      // 這是我們得到handler的過程  

      mappedHandler = getHandler(processedRequest, false);  

      if (mappedHandler == null || mappedHandler.getHandler() == null) {  

        noHandlerFound(processedRequest, response);  

        return;  

      }  

 

      // 這里取出執(zhí)行鏈中的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;  

        }  

      }  

 

      //在執(zhí)行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的?! ?/p>

      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  

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

 

      // 這里取出執(zhí)行鏈中的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框架緊密相關(guān)的代碼,比如如何得到和http請求相對應(yīng)的執(zhí)行鏈,怎樣執(zhí)行執(zhí)行鏈和怎樣把模型數(shù)據(jù)展現(xiàn)到視圖中去。

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

  代碼

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {  

 //在ServletContext取得執(zhí)行鏈 - 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存?! ?/p>

 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中建立的映射表進行檢索得到請求對應(yīng)的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是緩沖了上次處理的結(jié)果 - 總要有第一次把這個handler放到ServletContext中去:

  如果在ServletContext中找不到handler,那就通過持有的handlerMapping生成一個,我們看到它會迭代當(dāng)前持有的所有的 handlerMapping,因為可以定義不止一個,他們在定義的時候也可以指定順序,直到找到第一個,然后返回。先找到一個 handlerMapping,然后通過這個handlerMapping返回一個執(zhí)行鏈,里面包含了最終的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接口的實現(xiàn),比如對于具體的HandlerAdapter - SimpleControllerHandlerAdapter:

  代碼

public class SimpleControllerHandlerAdapter implements HandlerAdapter {  

   

  public boolean supports(Object handler) {  

    return (handler instanceof Controller);  

  }  

  .......  

} 

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

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

  代碼

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

  這個就是對handle的具體調(diào)用!相當(dāng)于Command模式里的Command.execute();理所當(dāng)然的返回一個ModelAndView,下面就是一個對View進行處理的過程:

  代碼

if (mv != null && !mv.wasCleared()) {  

  render(mv, processedRequest, response);  

} 

  調(diào)用的是render方法:

  代碼

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)  

     throws Exception {response.setLocale(locale);  

 

   View view = null;  

   //這里把默認(rèn)的視圖放到ModelAndView中去?! ?/p>

   if (!mv.hasView()) {  

     mv.setViewName(getDefaultViewName(request));  

   }  

 

   if (mv.isReference()) {  

     // 這里對視圖名字進行解析  

     view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);  

   .......  

   }  

   else {  

     // 有可能在ModelAndView里已經(jīng)直接包含了View對象,那我們就直接使用。  

     view = mv.getView();  

   ........  

   }  

 

   //得到具體的View對象以后,我們用它來生成視圖?! ?/p>

   view.render(mv.getModelInternal(), request, response);  

} 

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

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

  代碼

public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {  

  ......  

  // 這里把所有的相關(guān)信息都收集到一個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));  

  }  

  //這是實際的展現(xiàn)模型數(shù)據(jù)到視圖的調(diào)用?! ?/p>

  renderMergedOutputModel(mergedModel, request, response);  

}

  注解寫的很清楚了,先把所有的數(shù)據(jù)模型進行整合放到一個Map - mergedModel里,然后調(diào)用renderMergedOutputModel();這個renderMergedOutputModel是一個模板方法,他的實現(xiàn)在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定義的內(nèi)部資源路徑。

    String dispatcherPath = prepareForRendering(request, response);

    //這里把請求轉(zhuǎn)發(fā)到前面得到的內(nèi)部資源路徑中去。

    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");

    }

    .......

  }

  首先對模型數(shù)據(jù)進行處理,exposeModelAsRequestAttributes是在AbstractView中實現(xiàn)的,這個方法把 ModelAndView中的模型數(shù)據(jù)和其他request數(shù)據(jù)統(tǒng)統(tǒng)放到ServletContext當(dāng)中去,這樣整個模型數(shù)據(jù)就通過 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);

        .......

      }

    }

  }

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

  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特殊的數(shù)據(jù)處理和設(shè)置。

  過程是不是很長?我們現(xiàn)在在哪里了?呵呵,我們剛剛完成的事MVC中View的render,對于InternalResourceView的render 過程比較簡單只是完成一個資源的重定向處理。需要做的就是得到實際view的internalResource路徑,然后轉(zhuǎn)發(fā)到那個資源中去。怎樣得到資源的路徑呢通過調(diào)用:

  Java代碼

   protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)

      throws Exception {

    return getUrl();

  }

  那這個url在哪里生成呢?我們在View相關(guān)的代碼中沒有找到,實際上,他在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相關(guān)的屬性也配置好了。

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

  Java代碼

  protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)

      throws Exception {

    ........

    View view = null;

    // 這里設(shè)置視圖名為默認(rèn)的名字

    if (!mv.hasView()) {

      mv.setViewName(getDefaultViewName(request));

    }

    if (mv.isReference()) {

      //這里對視圖名進行解析,在解析的過程中根據(jù)需要生成實際需要的視圖對象。

      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;

  }

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

  Java代碼

  public View resolveViewName(String viewName, Locale locale) throws Exception {

    //如果沒有打開緩存設(shè)置,那創(chuàng)建需要的視圖

    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) {

          //如果在緩存中沒有找到,創(chuàng)建一個并把創(chuàng)建的放到緩存中去

          view = createView(viewName, locale);

          this.viewCache.put(cacheKey, view);

        ........

        }

        return view;

      }

    }

  }

關(guān)于這些createView(),loadView(),buildView()的關(guān)系,我們看看Eclipse里的call hiearchy

  然后我們回到view.render中完成數(shù)據(jù)的最終對httpResponse的寫入,比如在AbstractExcelView中的實現(xiàn):

  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在解析視圖名的時候就根據(jù)要求生成了視圖對象,包括在InternalResourceView中需要使用的url和其他各種和HTTP response相關(guān)的屬性都會寫保持在生成的視圖對象中,然后就直接調(diào)用視圖對象的render來完成數(shù)據(jù)的展示。

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

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

 

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Spring MVC詳細源碼解析(下篇)
學(xué)習(xí)SpringMVC
[Java] SpringMVC工作原理之一:DispatcherServlet
通俗易懂說SpringMVC
第五章 處理器攔截器詳解——跟著開濤學(xué)SpringMVC
別怕,手把手帶你撕、拉、扯下SpringMVC的外衣
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服