最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring MVC學習教程之視圖深入解析

 更新時間:2018年11月18日 11:26:20   作者:愛寶貝丶  
這篇文章主要給大家介紹了關(guān)于Spring MVC學習教程之視圖解析的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或使用spring mvc具有一定的參考學習價值,需要的朋友們下面來一起看看吧

前言

在RequestMappingHandlerAdapter對request進行了適配,并且調(diào)用了目標handler之后,其會返回一個ModelAndView對象,該對象中主要封裝了兩個屬性:view和model。其中view可以是字符串類型也可以是View類型,如果是字符串類型,則表示邏輯視圖名,如果是View類型,則其即為我們要轉(zhuǎn)換的目標view;這里model是一個Map類型的對象,其保存了渲染視圖所需要的屬性。本文主要講解Spring是如何通過用戶配置的ViewResolver來對視圖進行解析,并且聲稱頁面進行渲染的。

首先我們來看一個比較典型的ViewResolver配置:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 <property name="prefix" value="/WEB-INF/view/"/>
 <property name="suffix" value=".jsp"/>
</bean>

這里配置的ViewResolver是InternalResourceViewResolver,其主要有兩個屬性:prefix和suffix。在進行視圖解析時,如果ModelAndView中的view是字符串類型的,那么要解析的視圖存儲位置就通過“prefix + (String)view + suffix”的格式生成要解析的文件路徑,并且將其封裝為一個View對象,最后通過View對象來渲染具體的視圖。前面講到,ModelAndView中view也可以是View類型的,如果其是View類型的,那么這里就可以跳過第一步,直接使用其提供的View對象進行視圖解析了。

由上面的講解可以看出,對于視圖的解析可以分為兩個步驟:①解析邏輯視圖名;②渲染視圖。對應(yīng)于這兩步,Spring也抽象了兩個接口:ViewResolver和View,這兩個接口的聲明分別如下:

public interface ViewResolver {
 // 通過邏輯視圖名和用戶地區(qū)信息生成View對象
 View resolveViewName(String viewName, Locale locale) throws Exception;
}
public interface View {
 // 獲取返回值的contentType
 default String getContentType() {
 return null;
 }

 // 通過用戶提供的模型數(shù)據(jù)與視圖信息渲染視圖
 void render(@Nullable Map<String, ?> model, HttpServletRequest request, 
  HttpServletResponse response) throws Exception;
}

從上面兩個接口的聲明可以看出,ViewResolver的作用主要在于通過用戶提供的邏輯視圖名根據(jù)一定的策略生成一個View對象,而View接口則負責根據(jù)視圖信息和需要填充的模型數(shù)據(jù)進行視圖的渲染。這里我們首先看InternalResourceViewResolver是如何解析視圖名的,如下是其具體實現(xiàn)方式:

@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
 // 判斷當前ViewResolver是否設(shè)置了需要對需要解析的視圖進行緩存,如果不需要緩存,
 // 則每次請求時都會重新解析生成視圖對象
 if (!isCache()) {
  // 根據(jù)視圖名稱和用戶地區(qū)信息創(chuàng)建View對象
  return createView(viewName, locale);
 } else {
  // 如果可以對視圖進行緩存,則首先獲取緩存使用的key,然后從緩存中獲取該key,如果沒有取到,
  // 則對其進行加鎖,再次獲取,如果還是沒有取到,則創(chuàng)建一個新的View,并且對其進行緩存。
  // 這里使用的是雙檢查法來判斷緩存中是否存在對應(yīng)的邏輯視圖。
  Object cacheKey = getCacheKey(viewName, locale);
  View view = this.viewAccessCache.get(cacheKey);
  if (view == null) {
   synchronized (this.viewCreationCache) {
    view = this.viewCreationCache.get(cacheKey);
    if (view == null) {
     view = createView(viewName, locale);
     // 這里cacheUnresolved指的是是否緩存默認的空視圖,UNRESOLVED_VIEW是
     // 一個沒有任何內(nèi)容的View
     if (view == null && this.cacheUnresolved) {
      view = UNRESOLVED_VIEW;
     }
     if (view != null) {
      this.viewAccessCache.put(cacheKey, view);
      this.viewCreationCache.put(cacheKey, view);
      if (logger.isTraceEnabled()) {
       logger.trace("Cached view [" + cacheKey + "]");
      }
     }
    }
   }
  }
  return (view != UNRESOLVED_VIEW ? view : null);
 }
}

上面代碼中,InternalResourceViewResolver主要是判斷了當前是否配置了需要緩存生成的View對象,如果需要緩存,則從緩存中取,如果沒有配置,則每次請求時都會重新生成新的View對象。這里我們繼續(xù)看其是如何創(chuàng)建視圖的:

@Override
protected View loadView(String viewName, Locale locale) throws Exception {
 // 使用邏輯視圖名按照指定規(guī)則生成View對象
 AbstractUrlBasedView view = buildView(viewName);
 // 應(yīng)用聲明周期函數(shù),也就是調(diào)用View對象的初始化函數(shù)和Spring用于切入bean創(chuàng)建的
 // Processor和Aware函數(shù)
 View result = applyLifecycleMethods(viewName, view);
 // 檢查view的準確性,這里默認始終返回true
 return (view.checkResource(locale) ? result : null);
}

// 這里buildView()方法主要是根據(jù)邏輯視圖名生成一個View對象
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
 // 對于InternalResourceViewResolver而言,其返回的View對象的
 // 具體類型是InternalResourceView
 Class<?> viewClass = getViewClass();
 Assert.state(viewClass != null, "No view class");

 // 使用反射生成InternalResourceView對象實例
 AbstractUrlBasedView view = (AbstractUrlBasedView) 
  BeanUtils.instantiateClass(viewClass);
 // 這里可以看出,InternalResourceViewResolver獲取目標視圖的方式就是將用戶返回的
 // viewName與prefix和suffix進行拼接,以供View對象直接讀取
 view.setUrl(getPrefix() + viewName + getSuffix());

 // 設(shè)置View的contentType屬性
 String contentType = getContentType();
 if (contentType != null) {
  view.setContentType(contentType);
 }

 // 設(shè)置contextAttribute和attributeMap等屬性
 view.setRequestContextAttribute(getRequestContextAttribute());
 view.setAttributesMap(getAttributesMap());

 // 這了pathVariables表示request請求url中的屬性,這里主要是設(shè)置是否將這些屬性暴露到視圖中
 Boolean exposePathVariables = getExposePathVariables();
 if (exposePathVariables != null) {
  view.setExposePathVariables(exposePathVariables);
 }
 
 // 這里設(shè)置的是是否將Spring的bean暴露在視圖中,以供給前端調(diào)用
 Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
 if (exposeContextBeansAsAttributes != null) {
  view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
 }
 
 // 設(shè)置需要暴露給前端頁面的bean名稱
 String[] exposedContextBeanNames = getExposedContextBeanNames();
 if (exposedContextBeanNames != null) {
  view.setExposedContextBeanNames(exposedContextBeanNames);
 }

 return view;
}

protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {
 ApplicationContext context = getApplicationContext();
 if (context != null) {
  // 對生成的View對象應(yīng)用初始化方法,主要包括InitializingBean.afterProperties()和一些
  // Processor,Aware方法
  Object initialized = context.getAutowireCapableBeanFactory()
   .initializeBean(view, viewName);
  if (initialized instanceof View) {
   return (View) initialized;
  }
 }
 return view;
}

從上面對于視圖名稱的解析,可以看出,其主要做了四部分工作:①實例化View對象;②設(shè)置目標視圖地址;③初始化視圖的一些基本屬性,如需要暴露的bean對象;④調(diào)用View對象的初始化方法對其進行初始化。從這里的生成View對象的過程也可以看出,ViewResolver生成的View對象只是保存了目標view的地址,而對其加載和渲染的過程主要是委托給了View對象進行的。下面我們就來看一下InternalResourceView是如何結(jié)合具體的model來渲染視圖的:

@Override
public void render(@Nullable 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);
 }

 // 這里主要是將request中pathVariable,staticAttribute與用戶返回的model屬性
 // 合并為一個Map對象,以供給后面對視圖的渲染使用
 Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
 // 判斷當前View對象的類型是否為文件下載類型,如果是文件下載類型,則設(shè)置response的
 // Pragma和Cache-Control等屬性值
 prepareResponse(request, response);
 // 通過合并的model數(shù)據(jù)以及視圖地址進行視圖的渲染
 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

這里對于視圖的渲染主要分為了三步:①合并用戶返回的model數(shù)據(jù)和request中的pathVariable與staticAttribute等數(shù)據(jù);②判斷當前是否為文件下載類型的視圖解析,如果是,則設(shè)置Pragma和Cache-Control等header;③通過合并的模型數(shù)據(jù)和request請求對視圖進行渲染。這里我們主要看一下renderMergedOutputModel()方法是如何對視圖進行渲染的:

@Override
protected void renderMergedOutputModel(Map<String, Object> model, 
  HttpServletRequest request, HttpServletResponse response) throws Exception {
 // 這里主要是對model進行遍歷,將其key和value設(shè)置到request中,當做request的
 // 一個屬性供給頁面調(diào)用
 exposeModelAsRequestAttributes(model, request);
 // 提供的一個hook方法,默認是空實現(xiàn),用于用戶進行request屬性的自定義使用
 exposeHelpers(request);

 // 檢查當前是否存在循環(huán)類型的視圖名稱解析,主要是根據(jù)相對路徑進行判斷視圖名是無法解析的
 String dispatcherPath = prepareForRendering(request, response);
 
 // 獲取當前request的RequestDispatcher對象,該對象有兩個方法:include()和forward(),
 // 用于對當前的request進行轉(zhuǎn)發(fā),其實也就是將當前的request轉(zhuǎn)發(fā)到另一個url,這里的另一個
 // url就是要解析的視圖地址,也就是說進行視圖解析的時候請求的對于文件的解析實際上相當于
 // 構(gòu)造了另一個(文件)請求,在該請求中對文件內(nèi)容進行渲染,從而得到最終的文件。這里的
 // include()方法表示將目標文件引入到當前文件中,與jsp中的include標簽作用相同;
 // forward()請求則表示將當前請求轉(zhuǎn)發(fā)到另一個請求中,也就是目標文件路徑,這種轉(zhuǎn)發(fā)并不會
 // 改變用戶瀏覽器地址欄的請求地址。
 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!");
 }

 // 判斷當前是否為include請求,如果是,則調(diào)用RequestDispatcher.include()方法進行文件引入
 if (useInclude(request, response)) {
  response.setContentType(getContentType());
  if (logger.isDebugEnabled()) {
   logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" 
    + getBeanName() + "'");
  }
  rd.include(request, response);
 } else {
  if (logger.isDebugEnabled()) {
   logger.debug("Forwarding to resource [" + getUrl() 
    + "] in InternalResourceView '" + getBeanName() + "'");
  }
  // 如果當前不是include()請求,則直接使用forward請求將當前請求轉(zhuǎn)發(fā)到目標文件路徑中,
  // 從而渲染該視圖
  rd.forward(request, response);
 }
}

上述代碼就是進行視圖渲染的核心邏輯,上述邏輯主要分為兩個步驟:①將需要在頁面渲染使用的model數(shù)據(jù)設(shè)置到request中;②按照當前請求的方式(include或forward)來將當前請求轉(zhuǎn)發(fā)到目標文件中,從而達到目標文件的渲染。從這里可以看出,實際上對于Spring而言,其對頁面的渲染并不是在其原始的request中完成的。

本文首先講解了Spring進行視圖渲染所需要的兩大組件ViewResolver和View的關(guān)系,然后以InternalResourceViewResolver和InternalResourceView為例講解Spring底層是如何解析一個view,并且渲染該View的。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • java編程之單元測試(Junit)實例分析(附實例源碼)

    java編程之單元測試(Junit)實例分析(附實例源碼)

    這篇文章主要介紹了java編程之單元測試(Junit),結(jié)合實例形式較為詳細的分析總結(jié)了Java單元測試的原理、步驟及相關(guān)注意事項,并附帶了完整代碼供讀者下載參考,需要的朋友可以參考下
    2015-11-11
  • Java基礎(chǔ)之枚舉Enum類案例詳解

    Java基礎(chǔ)之枚舉Enum類案例詳解

    這篇文章主要介紹了Java基礎(chǔ)之枚舉Enum類案例詳解,文中有非常詳細的代碼示例,對正在學習java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java scala模式匹配機制詳解

    Java scala模式匹配機制詳解

    模式匹配語法中,采用match關(guān)鍵字聲明,每個分支采用case關(guān)鍵字進行聲明,當需要匹配時,會從第一個case分支開始,如果匹配成功,那么執(zhí)行對應(yīng)的邏輯代碼,如果匹配不成功,繼續(xù)執(zhí)行下一個分支進行判斷
    2023-02-02
  • mybatis返回的map結(jié)果如何設(shè)置有序

    mybatis返回的map結(jié)果如何設(shè)置有序

    這篇文章主要介紹了mybatis返回的map結(jié)果如何設(shè)置有序,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • Java多線程之同步鎖-lock詳解

    Java多線程之同步鎖-lock詳解

    這篇文章主要為大家詳細介紹了Java多線程中同步鎖-lock的原理與使用,文中的示例代碼講解詳細,對我們了解線程有一定幫助,需要的可以參考一下
    2022-10-10
  • JDK14之jpackage打包命令的使用

    JDK14之jpackage打包命令的使用

    這篇文章主要介紹了JDK14之jpackage打包命令的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • Intellij idea 代碼提示忽略字母大小寫和常用快捷鍵及設(shè)置步驟

    Intellij idea 代碼提示忽略字母大小寫和常用快捷鍵及設(shè)置步驟

    這篇文章主要介紹了Intellij idea 代碼提示忽略字母大小寫和常用快捷鍵及設(shè)置步驟,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • java實現(xiàn)的密碼強度檢測功能完整示例

    java實現(xiàn)的密碼強度檢測功能完整示例

    這篇文章主要介紹了java實現(xiàn)的密碼強度檢測功能,結(jié)合完整實例形式分析了java針對密碼強度檢測相關(guān)的字符串遍歷、判斷,以及輸出密碼強度等級相關(guān)操作技巧,需要的朋友可以參考下
    2019-06-06
  • 一個簡陋的java圖書管理系統(tǒng)

    一個簡陋的java圖書管理系統(tǒng)

    這篇文章主要為大家詳細介紹了一個簡陋的java圖書管理系統(tǒng),簡單的實現(xiàn)功能測試,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • spring boot如何基于JWT實現(xiàn)單點登錄詳解

    spring boot如何基于JWT實現(xiàn)單點登錄詳解

    這篇文章主要介紹了spring boot如何基于JWT實現(xiàn)單點登錄詳解,用戶只需登錄一次就能夠在這兩個系統(tǒng)中進行操作。很明顯這就是單點登錄(Single Sign-On)達到的效果,需要的朋友可以參考下
    2019-06-06

最新評論

迁西县| 岐山县| 新密市| 富裕县| 永川市| 朝阳市| 漳浦县| 廉江市| 南华县| 宜宾市| 湘潭县| 互助| 杭锦后旗| 旌德县| 磴口县| 阿荣旗| 澄城县| 潜江市| 子长县| 旬阳县| 宜阳县| 乾安县| 和平县| 平原县| 威宁| 石景山区| 淮滨县| 毕节市| 建宁县| 尉氏县| 集安市| 西林县| 临邑县| 得荣县| 大宁县| 华池县| 泸西县| 图们市| 宿州市| 岚皋县| 黎平县|