亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

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

2024-07-14 08:42:53
字體:
來源:轉載
供稿:網友

前言

在RequestMappingHandlerAdapter對request進行了適配,并且調用了目標handler之后,其會返回一個ModelAndView對象,該對象中主要封裝了兩個屬性:view和model。其中view可以是字符串類型也可以是View類型,如果是字符串類型,則表示邏輯視圖名,如果是View類型,則其即為我們要轉換的目標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對象進行視圖解析了。

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

public interface ViewResolver { // 通過邏輯視圖名和用戶地區信息生成View對象 View resolveViewName(String viewName, Locale locale) throws Exception;}
public interface View { // 獲取返回值的contentType default String getContentType() { return null; } // 通過用戶提供的模型數據與視圖信息渲染視圖 void render(@Nullable Map<String, ?> model, HttpServletRequest request,   HttpServletResponse response) throws Exception;}

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

@Override@Nullablepublic View resolveViewName(String viewName, Locale locale) throws Exception { // 判斷當前ViewResolver是否設置了需要對需要解析的視圖進行緩存,如果不需要緩存, // 則每次請求時都會重新解析生成視圖對象 if (!isCache()) {  // 根據視圖名稱和用戶地區信息創建View對象  return createView(viewName, locale); } else {  // 如果可以對視圖進行緩存,則首先獲取緩存使用的key,然后從緩存中獲取該key,如果沒有取到,  // 則對其進行加鎖,再次獲取,如果還是沒有取到,則創建一個新的View,并且對其進行緩存。  // 這里使用的是雙檢查法來判斷緩存中是否存在對應的邏輯視圖。  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是     // 一個沒有任何內容的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對象。這里我們繼續看其是如何創建視圖的:

@Overrideprotected View loadView(String viewName, Locale locale) throws Exception { // 使用邏輯視圖名按照指定規則生成View對象 AbstractUrlBasedView view = buildView(viewName); // 應用聲明周期函數,也就是調用View對象的初始化函數和Spring用于切入bean創建的 // Processor和Aware函數 View result = applyLifecycleMethods(viewName, view); // 檢查view的準確性,這里默認始終返回true return (view.checkResource(locale) ? result : null);}// 這里buildView()方法主要是根據邏輯視圖名生成一個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()); // 設置View的contentType屬性 String contentType = getContentType(); if (contentType != null) {  view.setContentType(contentType); } // 設置contextAttribute和attributeMap等屬性 view.setRequestContextAttribute(getRequestContextAttribute()); view.setAttributesMap(getAttributesMap()); // 這了pathVariables表示request請求url中的屬性,這里主要是設置是否將這些屬性暴露到視圖中 Boolean exposePathVariables = getExposePathVariables(); if (exposePathVariables != null) {  view.setExposePathVariables(exposePathVariables); }  // 這里設置的是是否將Spring的bean暴露在視圖中,以供給前端調用 Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes(); if (exposeContextBeansAsAttributes != null) {  view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes); }  // 設置需要暴露給前端頁面的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對象應用初始化方法,主要包括InitializingBean.afterProperties()和一些  // Processor,Aware方法  Object initialized = context.getAutowireCapableBeanFactory()   .initializeBean(view, viewName);  if (initialized instanceof View) {   return (View) initialized;  } } return view;}

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

@Overridepublic 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對象的類型是否為文件下載類型,如果是文件下載類型,則設置response的 // Pragma和Cache-Control等屬性值 prepareResponse(request, response); // 通過合并的model數據以及視圖地址進行視圖的渲染 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}

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

@Overrideprotected void renderMergedOutputModel(Map<String, Object> model,   HttpServletRequest request, HttpServletResponse response) throws Exception { // 這里主要是對model進行遍歷,將其key和value設置到request中,當做request的 // 一個屬性供給頁面調用 exposeModelAsRequestAttributes(model, request); // 提供的一個hook方法,默認是空實現,用于用戶進行request屬性的自定義使用 exposeHelpers(request); // 檢查當前是否存在循環類型的視圖名稱解析,主要是根據相對路徑進行判斷視圖名是無法解析的 String dispatcherPath = prepareForRendering(request, response);  // 獲取當前request的RequestDispatcher對象,該對象有兩個方法:include()和forward(), // 用于對當前的request進行轉發,其實也就是將當前的request轉發到另一個url,這里的另一個 // url就是要解析的視圖地址,也就是說進行視圖解析的時候請求的對于文件的解析實際上相當于 // 構造了另一個(文件)請求,在該請求中對文件內容進行渲染,從而得到最終的文件。這里的 // include()方法表示將目標文件引入到當前文件中,與jsp中的include標簽作用相同; // forward()請求則表示將當前請求轉發到另一個請求中,也就是目標文件路徑,這種轉發并不會 // 改變用戶瀏覽器地址欄的請求地址。 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請求,如果是,則調用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請求將當前請求轉發到目標文件路徑中,  // 從而渲染該視圖  rd.forward(request, response); }}

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

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

總結

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


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美日韩中文在线制服| 高跟丝袜一区二区三区| 亚洲精品美女在线观看播放| 深夜福利一区二区| 黑人精品xxx一区| 热久久免费视频精品| 亚洲美女喷白浆| 国产日产欧美精品| 91精品国产色综合久久不卡98| 亚洲成年网站在线观看| 福利精品视频在线| 亚洲精品国产欧美| 亚洲毛片在线免费观看| 久热精品视频在线| 成人黄色免费网站在线观看| 国产va免费精品高清在线观看| 日韩a**中文字幕| 国产精品成人免费视频| 国产噜噜噜噜噜久久久久久久久| 2019中文在线观看| 日韩成人激情影院| 国产精品久久久久久久天堂| 精品国产91久久久| 久久精品国产久精国产一老狼| 国产精品电影一区| 欧美在线视频一区二区| 亚洲天堂视频在线观看| 欧美午夜精品久久久久久浪潮| 日韩精品久久久久| 亚洲人成网站免费播放| 在线中文字幕日韩| 国产精品专区第二| 奇米成人av国产一区二区三区| 日本视频久久久| 国产成+人+综合+亚洲欧美丁香花| 国产高清在线不卡| 亚洲一区二区中文字幕| 亚洲电影免费在线观看| 久久久久久久久久久国产| 欧美日韩一区二区三区| 亚洲欧美日韩一区二区三区在线| 国产成人免费91av在线| 97免费视频在线播放| 久久午夜a级毛片| 国产第一区电影| 亚洲乱码一区av黑人高潮| 成人做爰www免费看视频网站| 亚洲精品一区av在线播放| 亚洲精品在线看| 91精品视频大全| 欧美性猛交xxxx黑人猛交| 日韩h在线观看| 久久久噜久噜久久综合| 亚洲男人天堂久| 久久久最新网址| 国产99久久久欧美黑人| 久久中文久久字幕| 亚洲欧美另类人妖| 亚洲一区二区在线| 亚洲欧美国产日韩天堂区| 国产精品久久久久久久午夜| 成人欧美一区二区三区黑人| 亚洲欧美日韩在线一区| 国产亚洲精品成人av久久ww| 狠狠色狠色综合曰曰| 欧美午夜性色大片在线观看| 精品亚洲一区二区三区在线播放| 国产精品日本精品| 国产一区二区av| 亚洲最大福利网| 日韩欧美精品网址| 亚洲欧洲成视频免费观看| 在线观看国产精品日韩av| 久久久久久久久久国产| 欧美一级片久久久久久久| 2024亚洲男人天堂| 色香阁99久久精品久久久| 欧美日韩在线观看视频| 成人xxxxx| 成人春色激情网| 亚洲国产欧美精品| 久久精品成人欧美大片古装| 国产婷婷成人久久av免费高清| 黑人精品xxx一区一二区| 最新69国产成人精品视频免费| 中文字幕亚洲欧美| 成人激情在线观看| 最近2019中文字幕一页二页| 国产精品av免费在线观看| 狠狠躁18三区二区一区| 亚洲美女动态图120秒| 亚洲精品美女久久久| 欧美激情视频一区二区三区不卡| 亚洲一区av在线播放| 亚洲性夜色噜噜噜7777| 热久久这里只有精品| 在线观看中文字幕亚洲| 日韩激情av在线播放| 久久人人爽亚洲精品天堂| 国产精品久久久久免费a∨| 欧美日韩亚洲精品一区二区三区| 日韩一中文字幕| 久久久久这里只有精品| 日韩美女视频免费在线观看| 国产精品极品在线| 亚洲a级在线播放观看| 久久精品99国产精品酒店日本| 亚洲人在线视频| 日韩在线观看免费全集电视剧网站| 久久久极品av| 精品国产精品自拍| 日韩欧中文字幕| 91成人福利在线| 亚洲第一页在线| 91久久国产精品| 精品日韩中文字幕| 国产精品露脸自拍| 日韩av一区在线| 日韩av综合网站| 91精品国产91久久久久久最新| 亚洲码在线观看| 亚洲r级在线观看| 欧美激情在线播放| 国产精品一区专区欧美日韩| 亚洲а∨天堂久久精品9966| 日韩精品视频免费| 亚洲女在线观看| 国产一区玩具在线观看| 欧美国产精品人人做人人爱| 91av视频在线播放| 亚洲奶大毛多的老太婆| www亚洲欧美| 精品免费在线视频| 2020久久国产精品| 2020国产精品视频| 国产+成+人+亚洲欧洲| 性欧美视频videos6一9| 免费av在线一区| 久久免费视频这里只有精品| 国产成人精品在线视频| 国产精品wwww| 一区二区欧美激情| 超碰97人人做人人爱少妇| 欧美精品激情视频| 日韩国产欧美精品一区二区三区| 国产精品毛片a∨一区二区三区|国| 国产精品网红直播| 亚洲欧美日韩国产精品| 亚洲最大福利网站| 欧美激情精品久久久久久蜜臀| 亚洲精品小视频在线观看| 亚洲欧洲国产精品| 日韩精品免费一线在线观看| 在线播放日韩精品| 精品欧美aⅴ在线网站| 国内自拍欧美激情| 国产精品视频一| 亚洲人成在线电影| 欧美激情一区二区三区成人| 欧美激情欧美激情在线五月| 欧美激情性做爰免费视频| 日本精品视频网站| 日韩美女毛茸茸| 中文字幕日韩在线播放|