day04-視圖和視圖解析器

来源:https://www.cnblogs.com/liyuelian/archive/2023/02/07/17099911.html
-Advertisement-
Play Games

視圖和視圖解析器 1.基本介紹 在SpringMVC中的目標方法,最終返回的都是一個視圖(有各種視圖) 註意,這裡的視圖是一個類對象,不是一個頁面!! 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種) 2.自定義視圖 2.1為什麼需要自定義視圖 在預設情況下,我們都是返回預設的視圖,然後返 ...


視圖和視圖解析器

1.基本介紹

  1. 在SpringMVC中的目標方法,最終返回的都是一個視圖(有各種視圖)

    註意,這裡的視圖是一個類對象,不是一個頁面!!

  2. 返回的視圖都會由一個視圖解析器來處理(視圖解析器有很多種)

2.自定義視圖

2.1為什麼需要自定義視圖

  1. 在預設情況下,我們都是返回預設的視圖,然後返回的視圖交由 SpringMVC 的 InternalResourcesViewResolver 預設視圖解析器來處理的:

    image-20230206213632558
  2. 在實際開發中,因為業務需求,我們有時候需要自定義視圖解析器

  3. 視圖解析器可以配置多個,按照指定的順序來對視圖進行解析。如果上一個視圖解析器不匹配,下一個視圖解析器就會去解析視圖,以此類推。

2.2應用實例

執行流程:

image-20230207171939607
  1. view.jsp,請求到 Handler

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>自定義視圖測試</title>
    </head>
    <body>
    <h1>自定義視圖測試</h1>
    <a href="goods/buy">點擊到自定義視圖</a>
    </body>
    </html>
    
  2. GoodsHandler.java

    package com.li.web.viewresolver;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author 李
     * @version 1.0
     */
    @RequestMapping(value = "/goods")
    @Controller
    public class GoodsHandler {
        @RequestMapping(value = "/buy")
        public String buy(){
            System.out.println("----------buy()---------");
            return "liView";//自定義視圖名
        }
    }
    
  3. 自定義視圖 MyView.java

    package com.li.web.viewresolver;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.view.AbstractView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;
    
    /**
     * @author 李
     * @version 1.0
     * 1. MyView 繼承了AbstractView,就可以作為了一個視圖使用
     * 2. @Component(value = "myView") ,該視圖會註入到容器中,id為 liView
     */
    @Component(value = "liView")
    public class MyView extends AbstractView {
        @Override
        protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            //1.完成視圖渲染
            System.out.println("進入到自己的視圖...");
    
            //2.並且確定我們要跳轉的頁面,如 /WEB-INF/pages/my_view.jsp
            /*
             * 1.下麵就是請求轉發到 /WEB-INF/pages/my_view.jsp
             * 2.該路徑會被springmvc解析成 /web工程路徑/WEB-INF/pages/my_view.jsp
             */
            request.getRequestDispatcher("/WEB-INF/pages/my_view.jsp")
                    .forward(request, response);
        }
    }
    
  4. 結果頁面 my_view.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>my_view</title>
    </head>
    <body>
    <h1>進入到my_view頁面</h1>
    <p>從自定義視圖來的...</p>
    </body>
    </html>
    
  5. springDispatcherServlet-servlet.xml 配置自定義視圖解析器

    <!--1.指定掃描的包-->
    <context:component-scan base-package="com.li.web"/>
    
    <!--2.配置視圖解析器[預設的視圖解析器]-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置屬性 suffix(尾碼) 和 prefix(首碼)-->
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    
    <!--3.
        3.1 配置自定義視圖解析器 BeanNameViewResolver
        3.2 BeanNameViewResolver 可以解析我們自定義的視圖
        3.3 屬性 order 表示視圖節解析器執行的順序,值越小優先順序越高
        3.4 order 的預設值為最低優先順序-LOWEST_PRECEDENCE
        3.5 預設的視圖解析器就是最低優先順序,因此我們的自定義解析器會先執行
     -->
    <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <property name="order" value="99"/>
    </bean>
    
    <!--4.加入兩個常規的配置-->
    <!--支持SpringMVC的高級功能,比如:JSR303校驗,映射動態請求-->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!--將SpringMVC不能處理的請求,交給tomcat處理,比如css,js-->
    <mvc:default-servlet-handler/>
    
  6. 測試,訪問view.jsp,點擊超鏈接

    image-20230207175529158
  7. 成功跳轉到 my_view.jsp

    image-20230207175601984
  8. 後臺輸出如下:說明整個執行流程如圖所示。

    image-20230207175629231

2.3創建自定義視圖的步驟

  1. 自定義一個視圖:創建一個 View 的 bean,該 bean 需要繼承自 AbstractView,並實現renderMergedOutputModel方法
  2. 並把自定義 View 加入到 IOC 容器中
  3. 自定義視圖的視圖解析器,使用 BeanNameViewResolver,這個視圖解析器也需要配置到 ioc 容器文件中
  4. BeanNameViewResolver 的調用優先順序需要設置一下,設置 order 比 Integer.MAX_VALUE 小的值,以確保其在預設的視圖解析器之前被調用

2.4Debug源碼-自定義視圖解析器執行流程

自定義視圖-工作流程:

  1. SpringMVC 調用目標方法,返回自定義 View 在 IOC 容器中的 id
  2. SpringMVC 調用 BeanNameViewResolver 視圖解析器:從 IOC 容器中獲取返回 id 值對應的 bean,即自定義的 View 的對象
  3. SpringMVC 調用自定義視圖的 renderMergedOutputModel 方法,渲染視圖
  4. 說明:如果 SpringMVC 調用 Handler 的目標方法時,返回的自定義 View ,在 IOC 容器中的 id 不存在,則仍然按照預設的視圖解析器機制處理。

Debug-01

(1)在GoodsHandler的目標方法中打上斷點:

image-20230207181443199

(2)點擊debug,訪問view.jsp,點擊超鏈接,可以看到後臺游標跳轉到斷點處:

image-20230207182111043

(3)在源碼 BeanNameViewResolver 的 resolveViewName 方法處打上斷點:

image-20230207181949587

(4)點擊Resume,游標跳轉到了這個斷點處,viewName 的值就是自定義視圖對象的 id:這裡完成視圖解析

image-20230207182357898

resolveViewName 方法如下:

@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws BeansException {
   //獲取ioc容器對象
   ApplicationContext context = obtainApplicationContext();
   //如果容器對象中不存在 目標方法返回的自定義視圖對象id
   if (!context.containsBean(viewName)) {
      // Allow for ViewResolver chaining...
      //就返回null,讓預設的視圖解析器處理該視圖
      return null;
   }
   //判斷自定義的視圖是不是 org.springframework.web.servlet.View 類型
   if (!context.isTypeMatch(viewName, View.class)) {
      //如果不是
      if (logger.isDebugEnabled()) {
         logger.debug("Found bean named '" + viewName + "' but it does not implement View");
      }
      // Since we're looking into the general ApplicationContext here,
      // let's accept this as a non-match and allow for chaining as well...
      return null;
   }
   //如果是,就返回這個自定義視圖對象
   return context.getBean(viewName, View.class);
}
image-20230207183628224

(5)在自定義視圖對象里打上斷點:

image-20230207183815264

(6)點擊 resume,游標跳轉到該斷點:在這裡完成視圖渲染,並轉發到結果頁面

image-20230207183923620 image-20230207184246555

(7)最後由 tomcat 將數據返回給客戶端:

image-20230207184459844

2.5Debug源碼-預設視圖解析器執行流程

將預設視圖解析器的優先順序調高:

image-20230207184950399

debug-02

(1)仍然在GoodsHandler中添加斷點:

image-20230207192822730

(2)瀏覽器訪問 view.jsp,可以看到後臺游標跳轉到了斷點處:

image-20230207193027494

(3)分別在預設視圖解析器(InternalResourceViewResolver)和自定義視圖解析器(BeanNameViewResolver) 中的方法中打上斷點:

image-20230207193313100 image-20230207193321152

(4)點擊resume,可以看到游標先跳到了預設視圖解析器的 buildView 方法中:因為預設解析器的優先順序在之前設置為最高。

image-20230207193631707

buildView 方法:

@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
   //根據目標方法返回的viewName創建一個View對象
   InternalResourceView view = (InternalResourceView) super.buildView(viewName);
   if (this.alwaysInclude != null) {
      view.setAlwaysInclude(this.alwaysInclude);
   }
   view.setPreventDispatchLoop(true);
   return view;
}

這個 View 對象的 url 是按照你配置的首碼和尾碼,拼接完成的 url

image-20230207194350281

(5)之後就會到該View對象進行視圖渲染,然後由Tomcat將數據返回給客戶端。

但是如果該url下沒有/WEB-INF/pages/liView.jsp文件,就會報錯:

image-20230207195118671

2.6Debug源碼-自定義View不存在,會走預設視圖解析機制

視圖解析器可以配置多個,按照指定的順序來對視圖進行解析。如果上一個視圖解析器不匹配,下一個視圖解析器就會去解析視圖,以此類推:

image-20230207195521100
  1. 在容器文件中,將預設的視圖解析器調用優先順序降低,提高自定義視圖解析器的調用優先順序。見2.2的容器文件配置

  2. 刪除2.2中的自定義視圖MyView.java。也就是說,自定義視圖解析器解析目標方法返回的視圖對象時,將會無法解析該視圖,因為它不存在。

  3. 這時就會去調用下一個優先順序的視圖解析器,即預設視圖解析器。

debug-03

(1)仍然在GoodsHandler中添加斷點:

image-20230207192822730

(2)瀏覽器訪問 view.jsp,可以看到後臺游標跳轉到了斷點處:

image-20230207193027494

(3)在自定義的視圖解析器 BeanNameViewResolver 中打上斷點:

image-20230207200552990

(4)點擊resume,可以看到游標跳轉到該斷點處:

image-20230207200655556

(5)因為在容器文件中找不到該視圖對象的id了,因此會進入第一個分支,方法直接返回 null

image-20230207200857797

(6)點擊step over,游標跳轉到中央控制器的 resolveViewName 方法中:

@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {
      //迴圈調用視圖解析器,直到某個視圖解析器返回的view不為null
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}
image-20230207201505383

因為自定義視圖解析器會返回 null,因此這裡進入第二次迴圈,由預設的視圖解析器去進行解析,然後返回對應的視圖:

image-20230207202159365

(7)在該方法中打上斷點,點擊 resume,可以看到此時 view 是由預設的視圖解析器返回的視圖對象,走的是預設機制。

image-20230207203001876

(8)下一個就按照預設機制拼接的 url 去訪問該頁面,併進行渲染。然後由Tomcat返回給客戶端。如果根據 url 找不到該頁面,就報404錯誤。


補充:如果預設視圖解析器優先順序高,自定義的視圖解析器優先順序低,但是預設視圖解析器返回的的View為null,這時候會繼續調用自定義的視圖解析器嗎?

答:事實上,預設視圖解析器返回的 View 不會為 null。

因為它是根據目標方法返回的字元串+你配置的前尾碼進行 url 的拼接。只要目標方法返回了一個字元串,預設視圖處理器就不會返回 null。

如果目標方法返回的是 null 呢?將會以目標方法的路徑名稱+配置的前尾碼作為尋找頁面的 url

因此在迴圈調用視圖處理器的時候,一旦迴圈到預設視圖處理器,就不會調用後面的自定義視圖解析器。

3.目標方法直接指定轉發或重定向

3.1使用實例

目標方法中指定轉發或者重定向:

  1. 預設返回的方式是請求轉發,然後用視圖處理器進行處理。
  2. 但是也可以在目標方法中直接指定重定向或者轉發的 url 的地址。
  3. 註意:如果指定重定向,則不能定向到 /WEB-INF 目錄中。因為該目錄為 Tomcat 的內部目錄。

例子

  1. view.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>請求轉發或重定向</title>
    </head>
    <body>
    <h1>請求轉發或重定向</h1>
    <a href="goods/order">測試在目標方法找中指定請求轉發或重定向</a>
    </body>
    </html>
    
  2. GoodsHandler.java

    package com.li.web.viewresolver;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author 李
     * @version 1.0
     */
    @RequestMapping(value = "/goods")
    @Controller
    public class GoodsHandler {
        //演示直接指定請求轉發或者重定向
        @RequestMapping(value = "/order")
        public String order() {
            System.out.println("=========order()=========");
            //請求轉發到 /WEB-INF/pages/my_view.jsp
            //下麵的路徑會被解析/web工程路徑/WEB-INF/pages/my_view.jsp
            return "forward:/WEB-INF/pages/my_view.jsp";
        }
    }
    
  3. 訪問 view.jsp,點擊超鏈接:

    image-20230207210928762
  4. 成功進入到請求轉發的頁面:

    image-20230207210953307

    請求轉發也可以轉發到WEB-INF目錄之外的頁面。

  5. 修改 GoodsHandler.java 的 order 方法:

    //演示直接指定請求轉發或者重定向
    @RequestMapping(value = "/order")
    public String order() {
        System.out.println("=========order()=========");
        //重定向
        //1.對於重定向來說,不能重定向到/WEB-INF/目錄下
        //2.redirect 為重定向的關鍵字
        //3./login.jsp 是在伺服器解析的,解析為 /web工程路徑/login.jsp
        return "redirect:/login.jsp";
    }
    
  6. redeployTomcat,訪問 view.jsp,點擊超鏈接,可以看到成功重定向到 login.jsp頁面

    image-20230207212618227 image-20230207212638759

3.2Debug-指定請求轉發流程分析


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:當協議、子功能變數名稱、主功能變數名稱、埠號中任意一個不相同時,都算作不同域。不同域之間相互請求資源,就算作“跨域”。 本文分享自華為雲社區《九種跨域方式實現原理咋回事》,作者:龍哥手記 一、什麼是跨域? 1.什麼是同源策略及其限制內容? 同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同 ...
  • 作者: 京東零售 陳震 一、 什麼是Backbone 在前端的發展道路中,前端框架元老之一jQuery對繁瑣的DOM操作進行了封裝,提供了鏈式調用、各類選擇器,屏蔽了不同瀏覽器寫法的差異性,但是前端開發過程中依然存在作用域污染、代碼復用度低、冗餘度高、數據和事件綁定煩瑣等痛點。 5年後,Backbo ...
  • 1.Api 自動導入 unplugin-auto-import自動引入 composition api,不需要再手動引入。(npm 地址) 下載 npm i -D unplugin-auto-import 配置 vite.config.ts import AutoImport from "unplu ...
  • 如果想實現chatGPT的網頁版,調用介面就可以了,但是如果需要聯繫上下文語境,就需要在傳遞的數據的時候進行下拼接 傳參的時候對所有的對話數據進行拼接,拼成下麵這樣 {"prompt":"(You:在嗎\n)這裡在哦,有什麼可以幫助你的嗎?(You:你這個系統多少錢\n)抱歉,您想知道什麼?這裡是客 ...
  • 摘要:jstat命令可以查看堆記憶體各部分的使用量,以及載入類的數量。 本文分享自華為雲社區《JVM之通過jstat命令進行查看堆記憶體使用情況》,作者:共飲一杯無 。 基本概念 jstat是JDK自帶的一個輕量級小工具。它位於java的bin目錄下,主要利用JVM內建的指令對Java應用程式的資源和性 ...
  • R2M分散式鎖原理可以理解為一條內容或者圖片+文字+鏈接的載體,常見的案例有鎖說明和分散式鎖選擇、r2m分散式鎖選擇、r2m分散式鎖原理,加鎖核心流程。 ...
  • 問題場景:當用戶量增大的時候,系統內的消息推送(比如 系統內簡訊推送,微信,釘釘,極光個推推送)將是我們常見業務場景。當系統中的用戶量逐漸增大時候,群發用戶消息的推送也將成為系統中致命的性能瓶頸。 消息推送緩衝隊列模型 意義: 構建大批量消息推送隊列,來解決大批量數據通過拉長時間隊列模式來換取性能提 ...
  • 一、認證組件 簡介: 登錄認證的限制 認證組件是drf框架給我們提供的認證介面,它能夠在請求進入視圖函數/類前進驗證(例如:認證用戶是否登錄),對不符合認證的請求進行攔截並返回校驗失敗的信息 (1)、登錄介面 # 認證是基於登錄的介面上面操作的 所以前戲編寫一個簡單的登錄介面 models.py c ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...