springboot2.0 快速使用 Thymeleaf
前面我已經介紹啦如何在spirngboot2.0中使用freemarker和jsp,今天我們來說一下如何在springboot2.0中如何使用Thymeleaf。關於Thymeleaf的使用方式和介紹請參考我寫的關於Thymeleaf 2篇部落格進行了解:thymeleaf基礎教程 - 搭建雜貨鋪專案環境(一) 和 thymeleaf基礎教程 - 閱讀官方教程(二)。
為了在開發過程中使我們的修改立刻生效我們需要在application.properties中配置spring.thymeleaf.cache = false
先給大家先看一下效果:
spirngboot2.0 使用 Thymeleaf 需要引入Thymeleaf start 依賴,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
如果要使用模板佈局需要單獨引入 thymeleaf-layout-dialect。
<dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency>
關於Thymeleaf 國際化的問題 如果不配置 spring.messages.basename 預設的會取 message.properties 中的資訊 如果需要自定義國際化的內容需要新增LocaleResolverConfig 配置類內容如下:
package com.lijunkui.thymeleaf; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @Configuration public class LocaleResolverConfig { @Bean(name="localeResolver") public LocaleResolver localeResolverBean() { return new SessionLocaleResolver(); } }
然後再Controller 中定義操作語言環境
如果沒有定義 message_en_US.properties 和 message_zh_CN.properties 會預設取message.properties中的資訊
如果 Locale = Locale.CHINA 就取 message_zh_CN.properties
如果 Locale = Locale.US 就取 message_en_US.properties。
如果Locale = Locale.US 沒有message_en_US.properties 但是有message_zh_CN.properties 取得是message_zh_CN.properties中的資訊 而不是message.properties中的資訊 這個很費解。
我在這裡採用官方的示例專案的商品列表稍微修改一下儘可能把常用的操作給大家進行展示,方便大家快速查閱。關於Thymeleaf的語法請檢視 thymeleaf基礎教程 - 閱讀官方教程(二)。樣式檔案參考thymeleaf基礎教程 - 搭建雜貨鋪專案環境(一)。使用Thymeleaf核心內容都在頁面上,但是也需要後臺資料支撐,下面我們下說明一下後臺提供資料程式碼。
使用者類
package com.lijunkui.thymeleaf.product.model;
public class User {
private String firstName = null;
private String lastName = null;
private String nationality = null;
private Integer age = null;
public User(final String firstName, final String lastName,
final String nationality, final Integer age) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.nationality = nationality;
this.age = age;
}
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getName() {
return this.firstName + " " + this.lastName;
}
public String getNationality() {
return this.nationality;
}
public Integer getAge() {
return this.age;
}
}
商品類
package com.lijunkui.thymeleaf.product.model;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class Product {
private Integer id = null;//商品id
private String name = null;//商品名稱
private BigDecimal price = null;//商品價格
private boolean inStock = false;//是否有貨
private List<Comment> comments = new ArrayList<Comment>();//評論
public Product() {
super();
}
public Product(final Integer id, final String name, final boolean inStock, final BigDecimal price) {
super();
this.id = id;
this.name = name;
this.price = price;
this.inStock = inStock;
}
public Integer getId() {
return this.id;
}
public void setId(final Integer id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
public BigDecimal getPrice() {
return this.price;
}
public void setPrice(final BigDecimal price) {
this.price = price;
}
public boolean isInStock() {
return this.inStock;
}
public void setInStock(final boolean inStock) {
this.inStock = inStock;
}
public List<Comment> getComments() {
return this.comments;
}
}
評論類
package com.lijunkui.thymeleaf.product.model;
public class Comment {
private Integer id = null;//評論id
private String text = null;//評論內容
public Comment() {
super();
}
public Comment(final Integer id, final String text) {
this.id = id;
this.text = text;
}
public Integer getId() {
return this.id;
}
public void setId(final Integer id) {
this.id = id;
}
public String getText() {
return this.text;
}
public void setText(final String text) {
this.text = text;
}
}
查詢商品資料的DAO這裡都是寫死的資料沒有什麼可說的。
package com.lijunkui.thymeleaf.product.repositories;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.lijunkui.thymeleaf.product.model.Comment;
import com.lijunkui.thymeleaf.product.model.Product;
public class ProductRepository {
private static final ProductRepository INSTANCE = new ProductRepository();
private final Map<Integer,Product> productsById;
public static ProductRepository getInstance() {
return INSTANCE;
}
private ProductRepository() {
super();
this.productsById = new LinkedHashMap<Integer, Product>();
final Product prod1 = new Product(1, "鮮甜羅勒", true, new BigDecimal("4.99"));
final Product prod2 = new Product(2, "義大利番茄", false, new BigDecimal("1.25"));
final Product prod3 = new Product(3, "黃柿子椒", true, new BigDecimal("2.50"));
final Product prod4 = new Product(4, "車達乳酪", true, new BigDecimal("18.75"));
final Product prod5 = new Product(5, "超純椰子油", true, new BigDecimal("6.34"));
final Product prod6 = new Product(6, "有機番茄醬", true, new BigDecimal("1.99"));
final Product prod7 = new Product(7, "全麥片麥片粥", true, new BigDecimal("3.07"));
final Product prod8 = new Product(8, "傳統西紅柿羅勒醬", true, new BigDecimal("2.58"));
final Product prod9 = new Product(9, "藜麥粉", true, new BigDecimal("3.02"));
final Product prod10 = new Product(10, "蜜柚汁", true, new BigDecimal("2.58"));
final Product prod11 = new Product(11, "100%純楓糖漿", true, new BigDecimal("5.98"));
final Product prod12 = new Product(12, "義大利麵醬", false, new BigDecimal("2.08"));
final Product prod13 = new Product(13, "香草酥麥片", false, new BigDecimal("1.75"));
final Product prod14 = new Product(14, "初榨橄欖油", false, new BigDecimal("5.01"));
final Product prod15 = new Product(15, "烤蒜義大利麵醬", true, new BigDecimal("2.40"));
final Product prod16 = new Product(16, "蔬菜濃湯罐頭", true, new BigDecimal("2.19"));
final Product prod17 = new Product(17, "杏仁乳1L", true, new BigDecimal("3.24"));
final Product prod18 = new Product(18, "有機雞肉野米湯", true, new BigDecimal("3.17"));
final Product prod19 = new Product(19, "紫胡蘿蔔、黑莓、藜麥和希臘酸奶", true, new BigDecimal("8.88"));
final Product prod20 = new Product(20, "南瓜胡蘿蔔胡蘿蔔汁", false, new BigDecimal("3.90"));
final Product prod21 = new Product(21, "有機菜籽油", true, new BigDecimal("10.13"));
final Product prod22 = new Product(22, "馬鈴薯玉米捲餅", true, new BigDecimal("2.44"));
final Product prod23 = new Product(23, "玉米雜燴湯罐頭", true, new BigDecimal("2.30"));
final Product prod24 = new Product(24, "有機檸檬汁", true, new BigDecimal("2.48"));
final Product prod25 = new Product(25, "辣羅勒醬", true, new BigDecimal("4.72"));
final Product prod26 = new Product(26, "甜龍舌蘭蜜", true, new BigDecimal("6.46"));
final Product prod27 = new Product(27, "黑烤花生醬", false, new BigDecimal("3.48"));
final Product prod28 = new Product(28, "無糖檸檬綠茶", true, new BigDecimal("18.34"));
final Product prod29 = new Product(29, "全穀物薄片穀物", true, new BigDecimal("3.52"));
final Product prod30 = new Product(30, "燕麥卷", true, new BigDecimal("4.00"));
this.productsById.put(prod1.getId(), prod1);
this.productsById.put(prod2.getId(), prod2);
this.productsById.put(prod3.getId(), prod3);
this.productsById.put(prod4.getId(), prod4);
this.productsById.put(prod5.getId(), prod5);
this.productsById.put(prod6.getId(), prod6);
this.productsById.put(prod7.getId(), prod7);
this.productsById.put(prod8.getId(), prod8);
this.productsById.put(prod9.getId(), prod9);
this.productsById.put(prod10.getId(), prod10);
this.productsById.put(prod11.getId(), prod11);
this.productsById.put(prod12.getId(), prod12);
this.productsById.put(prod13.getId(), prod13);
this.productsById.put(prod14.getId(), prod14);
this.productsById.put(prod15.getId(), prod15);
this.productsById.put(prod16.getId(), prod16);
this.productsById.put(prod17.getId(), prod17);
this.productsById.put(prod18.getId(), prod18);
this.productsById.put(prod19.getId(), prod19);
this.productsById.put(prod20.getId(), prod20);
this.productsById.put(prod21.getId(), prod21);
this.productsById.put(prod22.getId(), prod22);
this.productsById.put(prod23.getId(), prod23);
this.productsById.put(prod24.getId(), prod24);
this.productsById.put(prod25.getId(), prod25);
this.productsById.put(prod26.getId(), prod26);
this.productsById.put(prod27.getId(), prod27);
this.productsById.put(prod28.getId(), prod28);
this.productsById.put(prod29.getId(), prod29);
this.productsById.put(prod30.getId(), prod30);
prod2.getComments().add(new Comment(1, "I'm so sad this product is no longer available!"));
prod2.getComments().add(new Comment(2, "When do you expect to have it back?"));
prod13.getComments().add(new Comment(3, "Very tasty! I'd definitely buy it again!"));
prod13.getComments().add(new Comment(4, "My kids love it!"));
prod13.getComments().add(new Comment(5, "Good, my basic breakfast cereal. Though maybe a bit in the sweet side..."));
prod13.getComments().add(new Comment(6, "Not that I find it bad, but I think the vanilla flavouring is too intrusive"));
prod13.getComments().add(new Comment(7, "I agree with the excessive flavouring, but still one of my favourites!"));
prod13.getComments().add(new Comment(8, "Cheaper than at the local store!"));
prod13.getComments().add(new Comment(9, "I'm sorry to disagree, but IMO these are far too sweet"));
prod13.getComments().add(new Comment(10, "Good. Pricey though."));
prod9.getComments().add(new Comment(11, "Made bread with this and it was great!"));
prod9.getComments().add(new Comment(12, "Note: this comes actually mixed with wheat flour"));
prod14.getComments().add(new Comment(13, "Awesome Spanish oil. Buy it now."));
prod14.getComments().add(new Comment(14, "Would definitely buy it again. Best one I've tasted"));
prod14.getComments().add(new Comment(15, "A bit acid for my taste, but still a very nice one."));
prod14.getComments().add(new Comment(16, "Definitely not the average olive oil. Really good."));
prod16.getComments().add(new Comment(17, "Great value!"));
prod24.getComments().add(new Comment(18, "My favourite :)"));
prod30.getComments().add(new Comment(19, "Too hard! I would not buy again"));
prod30.getComments().add(new Comment(20, "Taste is OK, but I agree with previous comment that bars are too hard to eat"));
prod30.getComments().add(new Comment(21, "Would definitely NOT buy again. Simply unedible!"));
}
public List<com.lijunkui.thymeleaf.product.model.Product> findAll() {
return new ArrayList<Product>(this.productsById.values());
}
public Product findById(final Integer id) {
return this.productsById.get(id);
}
}
商品業務層類
package com.lijunkui.thymeleaf.product.service;
import java.util.List;
import com.lijunkui.thymeleaf.product.model.Product;
import com.lijunkui.thymeleaf.product.repositories.ProductRepository;
public class ProductService {
public ProductService() {
super();
}
public List<Product> findAll() {
return ProductRepository.getInstance().findAll();
}
public Product findById(final Integer id) {
return ProductRepository.getInstance().findById(id);
}
}
日曆的工具類
package com.lijunkui.thymeleaf.util;
import java.util.Calendar;
public class CalendarUtil {
public static Calendar calendarFor(
final int year, final int month, final int day, final int hour, final int minute) {
final Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1);
cal.set(Calendar.DAY_OF_MONTH, day);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
private CalendarUtil() {
super();
}
}
以上的程式碼是我們提供資料的基礎程式碼,內容並不複雜我們這裡就不過多解釋了
商品前臺訪問的控制器,後臺資料展示的核心主要是將所有商品列表資料,當前日期,以及登入使用者的資訊(這裡是寫死的模擬程式碼)。
package com.lijunkui.thymeleaf.product;
import java.util.Calendar;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.lijunkui.thymeleaf.product.model.Product;
import com.lijunkui.thymeleaf.product.model.User;
import com.lijunkui.thymeleaf.product.service.ProductService;
/**
* 商品Controller
* @author zhuoqianmingyue
*
*/
@Controller
public class ProductController {
private ProductService productService = new ProductService();
@RequestMapping("/")
public String useT(Model model,HttpServletRequest request) {
//獲取所有的商品
List<Product> allProducts = productService.findAll();
model.addAttribute("prods", allProducts);
//獲取當前日期
model.addAttribute("today", Calendar.getInstance());
//設定訪問使用者資訊到session
request.getSession(true).setAttribute("user", new User("桌前", "明月", "CHINA", null));
return "productList";
}
}
後臺application.properties thymeleaf配置資訊 和 thymeleaf 國際化配置資訊
#thymeleaf的編碼配置
spring.thymeleaf.encoding=UTF-8
#thymeleaf的快取設定 false是禁用快取
spring.thymeleaf.cache =false
#模板模式
spring.thymeleaf.mode=HTML5
#模板字尾名稱
spring.thymeleaf.suffix=.html
#模板路徑
spring.thymeleaf.prefix=classpath:/templates/
#國際化配置檔名稱 預設為messages
spring.messages.basename=message
#國際化配置的編碼
spring.messages.encoding=UTF-8
footer.html:頁面佈局的頁尾資訊
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
productList.html :商品列表頁面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>線上雜貨店</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" href="../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>
<body>
<div>
<img src="../../images/gtvglogo.png" alt="GTVG logo" title="GTVG logo"
th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />
</div>
<!-- -->
<p th:utext="#{home.welcome(${session.user.name})}">Welcome to our grocery store, Sebastian!</p>
<p>
Today is: <span th:with="df=#{date.format}" th:text="${#calendars.format(today,df)}">13 February 2011</span>
</p>
<h1>商品列表</h1>
<table>
<thead>
<tr>
<th>商品名稱</th>
<th>價格</th>
<th>是否有貨</th>
<th>評價</th>
</tr>
</thead>
<tbody th:remove="all-but-first">
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="../comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">檢視</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="../comments.html">檢視</a>
</td>
</tr>
</tbody>
</table>
<p>
<a href="../home.html" th:href="@{/}">返回首頁</a>
</p>
<div th:insert="~{footer::copy}">© 2011 The Static Templates</div>
</body>
</html>
message.properties :國際化預設配置資訊
true=yes
false=no
date.format=MMMM dd'','' yyyy
home.welcome=Welcome to our grocery store,{0}!(default message)
message_zh_CN.properties :國際化中文環境配置資訊
home.welcome=歡迎來到我們的雜貨店, {0}!
true=是
false=否
date.format=MMMM dd'','' yyyy
message_en_US.properties:國際化英文環境配置資訊
true=yes
false=no
date.format=MMMM dd'','' yyyy
home.welcome=Welcome to our grocery store,{0}!
專案檔案路徑如下圖 :
演示工具和版本說明:
JDK版本:1.8.0_144
springboot版本:2.0.5.RELEASE