SpringMVC返回視圖常見的 5 種方式,你會幾種?| SpringMVC系列第7篇
大家好,我是【路人甲 Java】號主路人,本文如果對你有幫助,點個在看,順便忙轉(zhuǎn)發(fā)一下,非常需要大家的支持,對 java 有興趣的朋友歡迎加我微信 itsoku 交流。
當(dāng) http 請求被自定義的 controller 處理時,如何指定響應(yīng)的頁面呢?
這個就是我們本文需要討論的問題。
在 controller 中響應(yīng)頁面有很多種方式,稍后我們會一一介紹,大家需要掌握每種方式的用法以及這些方式之間的區(qū)別,以后能夠靈活使用。
本文用到的頁面都以 jsp 為例,其他頁面模板技術(shù),比如 freemarker、velocity、thymeleaf、enjoy,這些我們后面專門再開篇講解。
1、本文內(nèi)容
SpringMVC 返回頁面的3種方式及區(qū)別 SpringMVC 重定向的2種方式及區(qū)別
2、軟件版本
idea 2020.3.3
jdk1.8
≥maven3.6.1
spring5.3.6
apache-tomcat-9.0.46
3、先來回顧下 servlet 中響應(yīng)頁面的 2 種方式
SpringMVC 底層是依靠 servlet 來實現(xiàn)的,所以我們先回顧下 servlet 中響應(yīng)頁面是如何實現(xiàn)的。
servlet 中響應(yīng)頁面有 2 種常見的方式,而 springmvc 中通常也是依靠這 2 種方式實現(xiàn)的。
方式 1:轉(zhuǎn)向
request.getRequestDispatcher(path).forward(request,response);
1、path 為轉(zhuǎn)向的地址
2、發(fā)生在服務(wù)器端,瀏覽器的地址欄不會發(fā)生變化
3、path 指定的頁面,可以共享 request 請求中的數(shù)據(jù)
4、path 必須是服務(wù)器端的資源
方式 2:重定向
response.sendRedirect(location);
1、location 為重定向的地址
2、重定向發(fā)生在客戶端(瀏覽器端),所以會導(dǎo)致瀏覽器地址欄發(fā)生變化,變?yōu)?location 指定的地址
3、重定向會導(dǎo)致瀏覽器重新向服務(wù)器端發(fā)生一次請求,請求地址為 location 指定的地址
4、location 可以為本服務(wù)器端的資源,也可以為外網(wǎng)可以訪問的任意資源,比如:http://www.baidu.com
下面來詳解 springmvc 中響應(yīng)頁面的 5 種方式。
4、方式 1:返回 ModelAndView
需求
通過 springmvc 實現(xiàn)用戶列表功能,如下圖

如何實現(xiàn)?
我們先來看一下如果用 servlet 是如何實現(xiàn)的,代碼如下:
1、List<UserDto> userList = new ArrayList();
2、request.setAttribute("userList",userList);
3、request.getRequestDispatcher("/WEB-INF/view/user/list.jsp").forward(request,response);
關(guān)鍵代碼就這幾行,相當(dāng)簡單。
對應(yīng)的 jsp(/WEB-INF/view/user/list.jsp)關(guān)鍵代碼如下,一個循環(huán)遍歷用戶列表 userList
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<th width="50">id</th>
<th width="100">name</th>
<th width="50">age</th>
</tr>
<c:forEach items="${userList}" var="user">
<tr align="center">
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>
</c:forEach>
</table>
使用 springmvc 實現(xiàn)
@Controller
public class UserController {
/**
* 用戶列表(用戶id->用戶信息)
*/
Map<Long, UserDto> userDtoMap = new ConcurrentHashMap<>();
{
userDtoMap.put(1L, new UserDto(1L, "路人", 30));
userDtoMap.put(2L, new UserDto(2L, "張三", 20));
userDtoMap.put(3L, new UserDto(3L, "李四", 18));
}
/**
* 用戶列表
*
* @return
*/
@RequestMapping("/user/list.do")
public ModelAndView list() {
//1.創(chuàng)建ModelAndView
ModelAndView modelAndView = new ModelAndView();
//2.將所有用戶信息放到Model中
modelAndView.addObject("userList", userDtoMap.values());
//3.設(shè)置顯示的頁面
modelAndView.setViewName("/WEB-INF/view/user/list.jsp");
//4.返回ModelAndView
return modelAndView;
}
}
這里主要看 list()這個方法,當(dāng)調(diào)用這個方法的時候,效果和上面 servlet 的效果一樣,這里用到了ModelAndView。
ModelAndView:模型&視圖
通常我們的頁面都是動態(tài)的,客戶端看到的頁面,基本上都是模板(視圖)+數(shù)據(jù)(數(shù)據(jù)模型),經(jīng)過組裝之后輸出到客戶端的。
所以響應(yīng)客戶端的請求,需要指定 2 個關(guān)鍵的信息:頁面、頁面中需要的數(shù)據(jù)。
springmvc 中使用 ModelAndView 來存放這 2 個信息,通過modelAndView.addObject方法添加頁面中用到的數(shù)據(jù),通過modelAndView.setViewName("視圖名稱")來設(shè)置顯示的頁面。
modelAndView.addObject("key","value")
添加頁面中需要用到的數(shù)據(jù),效果同:request.setAttribute("key","value");
modelAndView.setViewName("視圖名稱")
指定需要顯示的視圖命名,比如 jsp 地址
小結(jié)
如果頁面中需要用到一些動態(tài)的數(shù)據(jù),此時可以使用 ModelAndView 作為返回值,將動態(tài)數(shù)據(jù)放到 ModelAndView 中。
5、方式 2:直接返回視圖名稱
當(dāng)頁面不需要用到后端的數(shù)據(jù)的時候,就只是顯示一個頁面,此時可以直接將視圖的名稱作為返回值就可以了,比如
/**
* 跳轉(zhuǎn)到新增頁面
*
* @return
*/
@RequestMapping("/user/add.do")
public String add() {
//直接返回視圖的名稱(頁面的路徑)
return "/WEB-INF/view/user/add.jsp";
}

6、方式 3:指定視圖解析器
存在的問題
大家看下上面 2 種方式,返回的視圖名稱,都以/WEB-INF/view/開頭,以.jsp結(jié)尾對不對。
如果項目中我們規(guī)定所有的視圖都符合這種規(guī)則,即都放在/WEB-INF/view/目錄中,都是 jsp 文件,那么我們可以將視圖的名稱是不是可以簡化一下,怎么做的呢?
具體 2 個步驟。
step1:注冊視圖解析器
在 springmvc 配置文件中添加下面配置,來指定視圖解析器。
這個 bean 會對視圖的名稱進行處理,有 2 個參數(shù)需要指定
prefix:視圖文件前綴
suffix:視圖文件后綴
最終視圖的名稱 = prefix+controller 中指定的 viewname+suffix
<!-- 添加視圖解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
step2:調(diào)整 controller 中視圖的名稱
| viewName 舊值 | viewName 新值 |
|---|---|
| /WEB-INF/view/user/add.jsp | user/add |
| /WEB-INF/view/user/list.jsp | user/list |
@RequestMapping("/user/add.do")
public String add() {
//直接返回視圖的名稱(頁面的路徑)
return "user/add";
}
此時代碼是不是簡單多了。
7、SpringMVC 實現(xiàn)重定向 2 種方式
需求
有時候,請求之后,需要做重定向操作,比如發(fā)送刪除用戶信息的請求/user/del/{用戶id}.do,后端處理成功之后,需重定向到用戶列表頁面/user/list.do
這里就需要用到重定向的操作了,在 servlet 的中對應(yīng)代碼是
response.sendRedirect(location);
springmvc 中有好幾種實現(xiàn),這里我們主要掌握 2 種。
方式 1:返回 String 類型
springmvc 中實現(xiàn)重定向比較簡單,視圖的名稱必須需要以redirect:開頭,比如下面代碼,處理刪除用戶的請求,刪除成功之后,重定向到用戶列表頁面
/**
* 刪除用戶信息,刪除成功之后重定向到用戶列表頁
*
* @param userId 用戶id
* @return
*/
@GetMapping("/user/del/{userId}.do")
public String del(@PathVariable("userId") Long userId, HttpServletRequest request) {
//刪除用戶信息
this.userDtoMap.remove(userId);
//重定向到用戶列表頁面,此時瀏覽器地址會發(fā)生變化,變?yōu)閔ttp://localhost:8080/chat05/user/list.do
return "redirect:/user/list.do";
}
方式 2:返回 ModelAndView 類型
如果重定向的時候,我們需要向重定向的頁面攜帶參數(shù),一般我們可以這么做,代碼如下:
return "redirect:/user/list.do?在這里拼參數(shù)";
比如
return "redirect:/user/list.do?p1=v1&p2=v2";
如果遇到了這種請求,參數(shù)比較少的情況,按照上面拼接是可以的。
springmvc 中提供了更簡單的方式,代碼如下,最終 springmv 會指定將 ModelAndView 中添加的數(shù)據(jù),拼接到重定向的 url 中
@GetMapping("/user/del1/{userId}.do")
public ModelAndView del1(@PathVariable("userId") Long userId) {
//刪除用戶記錄
this.userDtoMap.remove(userId);
/**
* 重定向到用戶列表頁面,此時瀏覽器地址會發(fā)生變化,
* 變?yōu)閔ttp://localhost:8080/chat05/user/list.do?p1=v1&p2=v2
*/
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("p1", "v1");
modelAndView.addObject("p2", "v2");
modelAndView.setViewName("redirect:/user/list.do");
return modelAndView;
}
8、案例代碼
git 地址
https://gitee.com/javacode2018/springmvc-series

案例說明
案例中實現(xiàn)了用戶信息的增刪改查,用到了上面講到的所有技術(shù)。
用戶列表頁
http://localhost:8080/chat05/user/list.do

新增用戶頁面
http://localhost:8080/chat05/user/add.do

修改用戶信息頁面

刪除用戶信息
刪除用戶信息之后,會被重定向到用戶列表頁,案例中列出了 2 種刪除,用來模擬 2 種重定向的效果。

9、總結(jié)
響應(yīng)頁面通常有 2 種方式,第 1 種返回 ModelAndView,這種方式比較適合頁面中需要后端傳遞數(shù)據(jù)的,第 2 種方式直接返回視圖的名稱,這種適合無需傳遞數(shù)據(jù)的。
springmvc 容器中配置 InternalResourceViewResolver 視圖解析器,用來簡化 controller 中視圖的名稱
掌握重新的 2 種方式,重定向的關(guān)鍵點是視圖名稱要以
redirect:開頭,這樣 springmvc 才知道你需要 springmvc 來幫你執(zhí)行重定向操作。
10、SpringMVC 系列
SpringMVC 系列第 1 篇:helloword SpringMVC 系列第 2 篇:@Controller、@RequestMapping SpringMVC 系列第 3 篇:異常高效的一款接口測試?yán)?/a> SpringMVC 系列第 4 篇:controller 常見的接收參數(shù)的方式 SpringMVC 系列第 5 篇:@RequestBody 大解密,說點你不知道的 SpringMVC 系列第 6 篇:上傳文件的 4 種方式,你都會么?
11、更多好文章
Spring 高手系列(共 56 篇) Java 高并發(fā)系列(共 34 篇) MySql 高手系列(共 27 篇) Maven 高手系列(共 10 篇) Mybatis 系列(共 12 篇) 聊聊 db 和緩存一致性常見的實現(xiàn)方式 接口冪等性這么重要,它是什么?怎么實現(xiàn)? 泛型,有點難度,會讓很多人懵逼,那是因為你沒有看這篇文章!
12、推薦一個高質(zhì)量的公眾號
大家平時在學(xué)習(xí)技術(shù)的過程中,苦于找不到高質(zhì)量的學(xué)習(xí)資料的,可以關(guān)注一下【Java 充電社】,這個號專注于為大家提供高質(zhì)量的學(xué)習(xí)資源,已發(fā)布了大量高質(zhì)量的學(xué)習(xí)視頻、及資源,大家可以關(guān)注下。
