視圖和視圖決議器
1.基本介紹
-
在SpringMVC中的目標方法,最侄訓傳的都是一個視圖(有各種視圖)
注意,這里的視圖是一個類物件,不是一個頁面!!
-
回傳的視圖都會由一個視圖決議器來處理(視圖決議器有很多種)
2.自定義視圖
2.1為什么需要自定義視圖
-
在默認情況下,我們都是回傳默認的視圖,然后回傳的視圖交由 SpringMVC 的
InternalResourcesViewResolver默認視圖決議器來處理的:
-
在實際開發中,因為業務需求,我們有時候需要自定義視圖決議器
-
視圖決議器可以配置多個,按照指定的順序來對視圖進行決議,如果上一個視圖決議器不匹配,下一個視圖決議器就會去決議視圖,以此類推,
2.2應用實體
執行流程:
-
view.jsp,請求到 Handler
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>自定義視圖測驗</title> </head> <body> <h1>自定義視圖測驗</h1> <a href="https://www.cnblogs.com/liyuelian/archive/2023/02/07/goods/buy">點擊到自定義視圖</a> </body> </html> -
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 = "https://www.cnblogs.com/goods") @Controller public class GoodsHandler { @RequestMapping(value = "https://www.cnblogs.com/buy") public String buy(){ System.out.println("----------buy()---------"); return "liView";//自定義視圖名 } } -
自定義視圖 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 = "https://www.cnblogs.com/liyuelian/archive/2023/02/07/myView") ,該視圖會注入到容器中,id為 liView */ @Component(value = "https://www.cnblogs.com/liyuelian/archive/2023/02/07/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); } } -
結果頁面 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> -
springDispatcherServlet-servlet.xml 配置自定義視圖決議器
<!--1.指定掃描的包--> <context:component-scan base-package="com.li.web"/> <!--2.配置視圖決議器[默認的視圖決議器]--> <bean > <!--配置屬性 suffix(后綴) 和 prefix(前綴)--> <property name="prefix" value="https://www.cnblogs.com/WEB-INF/pages/"/> <property name="suffix" value="https://www.cnblogs.com/liyuelian/archive/2023/02/07/.jsp"/> </bean> <!--3. 3.1 配置自定義視圖決議器 BeanNameViewResolver 3.2 BeanNameViewResolver 可以決議我們自定義的視圖 3.3 屬性 order 表示視圖節決議器執行的順序,值越小優先級越高 3.4 order 的默認值為最低優先級-LOWEST_PRECEDENCE 3.5 默認的視圖決議器就是最低優先級,因此我們的自定義決議器會先執行 --> <bean > <property name="order" value="https://www.cnblogs.com/liyuelian/archive/2023/02/07/99"/> </bean> <!--4.加入兩個常規的配置--> <!--支持SpringMVC的高級功能,比如:JSR303校驗,映射動態請求--> <mvc:annotation-driven></mvc:annotation-driven> <!--將SpringMVC不能處理的請求,交給tomcat處理,比如css,js--> <mvc:default-servlet-handler/> -
測驗,訪問view.jsp,點擊超鏈接
-
成功跳轉到 my_view.jsp
-
后臺輸出如下:說明整個執行流程如圖所示,
2.3創建自定義視圖的步驟
- 自定義一個視圖:創建一個 View 的 bean,該 bean 需要繼承自 AbstractView,并實作renderMergedOutputModel方法
- 并把自定義 View 加入到 IOC 容器中
- 自定義視圖的視圖決議器,使用 BeanNameViewResolver,這個視圖決議器也需要配置到 ioc 容器檔案中
- BeanNameViewResolver 的呼叫優先級需要設定一下,設定 order 比 Integer.MAX_VALUE 小的值,以確保其在默認的視圖決議器之前被呼叫
2.4Debug原始碼-自定義視圖決議器執行流程
自定義視圖-作業流程:
- SpringMVC 呼叫目標方法,回傳自定義 View 在 IOC 容器中的 id
- SpringMVC 呼叫 BeanNameViewResolver 視圖決議器:從 IOC 容器中獲取回傳 id 值對應的 bean,即自定義的 View 的物件
- SpringMVC 呼叫自定義視圖的 renderMergedOutputModel 方法,渲染視圖
- 說明:如果 SpringMVC 呼叫 Handler 的目標方法時,回傳的自定義 View ,在 IOC 容器中的 id 不存在,則仍然按照默認的視圖決議器機制處理,
Debug-01
(1)在GoodsHandler的目標方法中打上斷點:
(2)點擊debug,訪問view.jsp,點擊超鏈接,可以看到后臺游標跳轉到斷點處:
(3)在原始碼 BeanNameViewResolver 的 resolveViewName 方法處打上斷點:
(4)點擊Resume,游標跳轉到了這個斷點處,viewName 的值就是自定義視圖物件的 id:這里完成視圖決議
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);
}
(5)在自定義視圖物件里打上斷點:
(6)點擊 resume,游標跳轉到該斷點:在這里完成視圖渲染,并轉發到結果頁面
(7)最后由 tomcat 將資料回傳給客戶端:
2.5Debug原始碼-默認視圖決議器執行流程
將默認視圖決議器的優先級調高:
debug-02
(1)仍然在GoodsHandler中添加斷點:
(2)瀏覽器訪問 view.jsp,可以看到后臺游標跳轉到了斷點處:
(3)分別在默認視圖決議器(InternalResourceViewResolver)和自定義視圖決議器(BeanNameViewResolver) 中的方法中打上斷點:
(4)點擊resume,可以看到游標先跳到了默認視圖決議器的 buildView 方法中:因為默認決議器的優先級在之前設定為最高,
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
(5)之后就會到該View物件進行視圖渲染,然后由Tomcat將資料回傳給客戶端,
但是如果該url下沒有/WEB-INF/pages/liView.jsp檔案,就會報錯:
2.6Debug原始碼-自定義View不存在,會走默認視圖決議機制
視圖決議器可以配置多個,按照指定的順序來對視圖進行決議,如果上一個視圖決議器不匹配,下一個視圖決議器就會去決議視圖,以此類推:
-
在容器檔案中,將默認的視圖決議器呼叫優先級降低,提高自定義視圖決議器的呼叫優先級,見2.2的容器檔案配置
-
洗掉2.2中的自定義視圖MyView.java,也就是說,自定義視圖決議器決議目標方法回傳的視圖物件時,將會無法決議該視圖,因為它不存在,
-
這時就會去呼叫下一個優先級的視圖決議器,即默認視圖決議器,
debug-03
(1)仍然在GoodsHandler中添加斷點:
(2)瀏覽器訪問 view.jsp,可以看到后臺游標跳轉到了斷點處:
(3)在自定義的視圖決議器 BeanNameViewResolver 中打上斷點:
(4)點擊resume,可以看到游標跳轉到該斷點處:
(5)因為在容器檔案中找不到該視圖物件的id了,因此會進入第一個分支,方法直接回傳 null
(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;
}
因為自定義視圖決議器會回傳 null,因此這里進入第二次回圈,由默認的視圖決議器去進行決議,然后回傳對應的視圖:
(7)在該方法中打上斷點,點擊 resume,可以看到此時 view 是由默認的視圖決議器回傳的視圖物件,走的是默認機制,
(8)下一個就按照默認機制拼接的 url 去訪問該頁面,并進行渲染,然后由Tomcat回傳給客戶端,如果根據 url 找不到該頁面,就報404錯誤,
補充:如果默認視圖決議器優先級高,自定義的視圖決議器優先級低,但是默認視圖決議器回傳的的View為null,這時候會繼續呼叫自定義的視圖決議器嗎?
答:事實上,默認視圖決議器回傳的 View 不會為 null,
因為它是根據目標方法回傳的字串+你配置的前后綴進行 url 的拼接,只要目標方法回傳了一個字串,默認視圖處理器就不會回傳 null,
如果目標方法回傳的是 null 呢?將會以目標方法的路徑名稱+配置的前后綴作為尋找頁面的 url
因此在回圈呼叫視圖處理器的時候,一旦回圈到默認視圖處理器,就不會呼叫后面的自定義視圖決議器,
3.目標方法直接指定轉發或重定向
3.1使用實體
目標方法中指定轉發或者重定向:
- 默認回傳的方式是請求轉發,然后用視圖處理器進行處理,
- 但是也可以在目標方法中直接指定重定向或者轉發的 url 的地址,
- 注意:如果指定重定向,則不能定向到 /WEB-INF 目錄中,因為該目錄為 Tomcat 的內部目錄,
例子
-
view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>請求轉發或重定向</title> </head> <body> <h1>請求轉發或重定向</h1> <a href="https://www.cnblogs.com/liyuelian/archive/2023/02/07/goods/order">測驗在目標方法找中指定請求轉發或重定向</a> </body> </html> -
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 = "https://www.cnblogs.com/goods") @Controller public class GoodsHandler { //演示直接指定請求轉發或者重定向 @RequestMapping(value = "https://www.cnblogs.com/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"; } } -
訪問 view.jsp,點擊超鏈接:
-
成功進入到請求轉發的頁面:
請求轉發也可以轉發到WEB-INF目錄之外的頁面,
-
修改 GoodsHandler.java 的 order 方法:
//演示直接指定請求轉發或者重定向 @RequestMapping(value = "https://www.cnblogs.com/order") public String order() { System.out.println("=========order()========="); //重定向 //1.對于重定向來說,不能重定向到/WEB-INF/目錄下 //2.redirect 為重定向的關鍵字 //3./login.jsp 是在服務器決議的,決議為 /web工程路徑/login.jsp return "redirect:/login.jsp"; } -
redeployTomcat,訪問 view.jsp,點擊超鏈接,可以看到成功重定向到 login.jsp頁面
3.2Debug-指定請求轉發流程分析
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/543213.html
標籤:其他
