Thymeleaf & 資料傳遞
Spring Boot 支援了許多模板引擎,如Thymeleaf、FreeMarker、Mustache、Velocity 等,其中Thymeleaf 是Spring 官方推薦使用的模板引擎,它的各項優點可以閱讀其他文章了解,這邊就不再贅述,但在開始講Thymeleaf 的使用方法前,想先介紹Spring MVC 如何傳遞資料給模板引擎。
在MVC 的架構下,該如何將Model (模型資料)傳遞給View (視圖模板)是其中一項重要的工作,Spring MVC 提供了許多方法可以將資料傳遞給模板引擎,如Model、ModelMap、ModelAndView、@ModelAttribute 及@SessionAttributes,下面會逐一介紹它們的使用方法。
引用與配置
pom.xml
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.properties 常用配置
# 表示是否啟用Thymeleaf 的快取,開發階段建議關閉
spring.thymeleaf.cache=false
# 前綴設置,預設即為classpath:/templates/
spring.thymeleaf.prefix=classpath:/templates/
# 後綴設置,預設為.html
spring.thymeleaf.suffix=.html
Model、ModelMap、ModelAndView
在開始使用Thymeleaf 前,一定要先了解一下Spring Boot 向Thymeleaf 模板傳遞資料的方法,Model、ModelMap、ModelAndView 就是其中基礎的三種資料傳遞方式,下面先列出範例頁面的HTML 檔案及Controller 的程式碼再進行解釋。
範例頁面 (model.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 接收資料的Key 值為message -->
<div>[[${message}]]</div>
</body>
</html>
Model
Controller
@GetMapping(value = "/model")
// 將Model 作為Controller 的引數,由Spring 框架自動創建並作為參數傳入
public String model(Model model) {
// 設定傳遞資料
model.addAttribute("message", "Hello thymeleaf. (using Model)");
// 返回值指定頁面路徑
return "model";
}
頁面
ModelMap
Controller
@GetMapping(value = "/modelMap")
// 將ModelMap 作為Controller 的引數,由Spring 框架自動創建並作為參數傳入
public String modelMap(ModelMap modelMap) {
// 設定傳遞資料
modelMap.addAttribute("message", "Hello thymeleaf. (using ModelMap)");
// 返回值指定頁面路徑
return "model";
}
頁面
ModelAndView
Controller
@GetMapping(value = "/modelAndView")
public ModelAndView modelAndAView() {
// 創建ModelAndView 物件時,可以順便指定頁面路徑
ModelAndView mav = new ModelAndView("model");
// 創建時不指定路徑則需要使用setViewName 方法進行設定
// ModelAndView mav = new ModelAndView();
// mav.setViewName("model");
// 設定傳遞資料
mav.addObject("message", "Hello thymeleaf. (using ModelAndView)");
return mav;
}
頁面
Model 和ModelMap
由上面Controller 的程式碼應該大概感覺得到,Model 和ModelMap 的用法是比較相似的,不管是創建的方法還是設定傳遞資料的方法都是一樣的,但其實在網路上對於這兩者的定義有所不同,部分查到的說法是Model 是用來接收各種型別的資料,如果接收一組或多個資料時則是ModelMap。
這裡畫個關係圖來解釋,Model 其實是個介面,它的實作類是ExtendedModelMap,而ExtendedModelMap 又繼承了ModelMap,就它們的關係以及個人使用經驗上,筆者個人是覺得兩者沒有差異。
ModelAndView 和Model、ModelMap
如果說Model 和ModelMap 是親戚關係的話,ModelAndView 則與它們只是同姓而已,在使用上,ModelAndView 與另外兩者的差異主要在於以下兩點。
1. ModelAndView 需要自己手動建立物件,不能作為方法參數交由Spring 框架創建傳入,設定傳遞資料的方法也不是addAttribute 而是addObject。
2. ModelAndView 可以作為方法的回傳值,不需要將目標頁面路徑作為回傳值,但需要在創建時指定路徑,或使用setViewName 方法指定路徑,若沒指定頁面路徑,則會拋出TemplateInputException。
@ModelAttribute
眾所皆知,Spring Boot 為了降低開發難度,有許多註釋可以用來輔助實現類別的定義或功能的開發,在Thymeleaf 也不例外,有@ModelAttribute 這個註釋可以簡化上述Model 的使用。
@ModelAttribute 比較常用在傳遞物件的情境上,相關的使用方式也分為了兩種,一種是定義在方法參數上,另一種則是定義在方法上。
方法參數
以下面範例所示,作為方法參數使用時,相當於在該方法加上model.addAttribute(),預設是以物件名稱小駝峰作為傳遞的資料名稱,若有另外指定則以指定名稱為主。
範例頁面,不指定名稱 (modelAttribute.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>[[${message}]]</div>
<div>[[${itemDto}]]</div>
<div>[[${itemDto.itemName}]]</div>
<div>[[${itemDto.itemDescription}]]</div>
</body>
</html>
範例頁面,指定名稱 (customModelAttribute.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>[[${message}]]</div>
<div>[[${myItem}]]</div>
<div>[[${myItem.itemName}]]</div>
<div>[[${myItem.itemDescription}]]</div>
</body>
</html>
範例物件 (ItemDto.java)
@Data
public class ItemDto {
private String itemName;
private String itemDescription;
}
Controller
@GetMapping(value = "/modelAttribute")
// 標記在方法參數上
public String modelAttribute1(@ModelAttribute Item item) {
// 相當於model.addAttribute("item", new Item());
itemDto.setItemName("item1");
itemDto.setItemDescription("description1");
return "modelAttribute";
}
@GetMapping(value = "/customModelAttribute")
// 標記在方法參數上並指定名稱
public String customModelAttribute(@ModelAttribute("myItem") ItemDto itemDto) {
// 相當於model.addAttribute("myItem", new ItemDto());
itemDto.setItemName("item2");
itemDto.setItemDescription("description2");
return "customModelAttribute";
}
頁面
方法層級
在方法上使用@ModelAttribute 註釋,則相當於為這個控制器下,所有使用@RequestMapping 定義的其他方法都加上@ModelAttribute 的方法內容。
Controller
@GetMapping(value = "/modelAttribute1")
public String modelAttribute1() {
return "model";
}
@GetMapping(value = "/modelAttribute2")
public String modelAttribute2() {
return "model";
}
@ModelAttribute
public void addAttributes(Model model) {
// 相當於為上面兩個方法 (modelAttribute1 和modelAttribut2)加上model.addAttribute("message", "Method Level.");
model.addAttribute("message", "Method Level.");
}
頁面
由於modelAttribute1 和modelAttribute2 方法內容並無變化,因此兩個呈現出來的頁面都是一樣的。
參考網站
https://www.796t.com/content/1541936173.html
https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation