Spring Boot Thymeleaf Model ModelMap ModelAndView ModelAttribute

Thymeleaf & 資料傳遞

詹凱鈞 Kaijun Chan 2022/09/15 14:14:32
5245

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 模板傳遞資料的方法,ModelModelMapModelAndView 就是其中基礎的三種資料傳遞方式,下面先列出範例頁面的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://lujianyun06.github.io/2020/04/13/java/spring/Model%E3%80%81ModelMap%E3%80%81ModelAndView%E7%9A%84%E4%BD%BF%E7%94%A8%E5%92%8C%E5%8C%BA%E5%88%AB/

https://www.baeldung.com/spring-mvc-and-the-modelattribute-annotation

詹凱鈞 Kaijun Chan