JSP在内嵌Servlet容器上運作有些問題(内嵌Tomcate、Jetty不支援以jar的形式運作JSP,Undertow不支援JSP)。
Spring Boot 預設使用Thymeleaf模闆,因為Thymeleaf模闆提供了完美的Spring MVC支援,可以完全替代JSP。
1 Thymeleaf基礎知識
Thymeleaf 是一個跟 Velocity、FreeMarker 類似的模闆引擎,它可以完全替代 JSP 。相較與其他的模闆引擎,它有如下三個極吸引人的特點:
- Thymeleaf 在有網絡和無網絡的環境下皆可運作,即它可以讓美工在浏覽器檢視頁面的靜态效果,也可以讓程式員在伺服器檢視帶資料的動态頁面效果。這是由于它支援 html 原型,然後在 html 标簽裡增加額外的屬性來達到模闆+資料的展示方式。浏覽器解釋 html 時會忽略未定義的标簽屬性,是以 thymeleaf 的模闆可以靜态地運作;當有資料傳回到頁面時,Thymeleaf 标簽會動态地替換掉靜态内容,使頁面動态顯示。
- Thymeleaf 開箱即用的特性。它提供标準和spring标準兩種方言,可以直接套用模闆實作JSTL、 OGNL表達式效果,避免每天套模闆、該jstl、改标簽的困擾。同時開發人員也可以擴充和建立自定義的方言。
- Thymeleaf 提供spring标準方言和一個與 SpringMVC 完美內建的可選子產品,可以快速的實作表單綁定、屬性編輯器、國際化等功能。
Thymeleaf 是一個Java類庫,它是一個xml/xhtml/html5的模闆引擎,可以作為MVC的Web應用的View層。
示例
<html xmlns:th="http://www.thymeleaf.org"><!-- 通過這個命名空間将靜态視圖轉換成動态的,需要動态的處理的元素前面加上th: -->
<head>
<meta content="text/html;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link th:href="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet"/> <!-- 通過@{}來對靜态資源的引用 -->
<link th:href="@{bootstrap/css/bootstrap-theme.min.css}" rel="stylesheet"/>
</head>
<body>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">通路model</h3>
</div>
<div class="panel-body">
<!-- ${}通路Mode中的屬性(注解要加上th: 字首) -->
<span th:text="${singlePerson.name}"></span>
</div>
</div>
<div th:if="${not #lists.isEmpty(people)}"> <!-- if判斷(<、>、<=、>=、==、 !=、也支援SpringEL) -->
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">清單</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<!-- 疊代 -->
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
<button class="btn" th:onclick="'getName(\'' + ${person.name} + '\');'">獲得名字</button>
</li>
</ul>
</div>
</div>
</div>
<script th:src="@{jquery-1.10.2.min.js}" type="text/javascript"></script><!-- 2 -->
<script th:src="@{bootstrap/js/bootstrap.min.js}"></script><!-- 2 -->
<script th:inline="javascript"> // th:inline="javascript" 添加到script标簽可以通路mdel中的屬性
var single = [[${singlePerson}]];
console.log(single.name+"/"+single.age)
function getName(name){
console.log(name);
}
</script>
</body>
</html>
2 與Spring MVC的內建
在Spring MVC中我們如果要內建一個模闆引擎的話,需要定義ViewResolver,而ViewResolver需要定義一個View,如我們為jsp定義的ViewResolver:
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/classes/views/");// 運作時代碼會将頁面自動編譯到/WEB-INF/classes/views/下
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
return viewResolver;
}
使用JstlView定義了一個InternalResourceViewResolver。是以使用Thymeleaf作為我的模闆也需要做類似的定義。Thymeleaf為我們提供了org.thymeleaf.spring4.view.ThymeleafView和org.thymeleaf.spring4.view.ThymeleafViewResolver(預設使用ThymeleafView作為View)。Thymeleaf為我們提供了SpringTemplateEngine類,用來驅動在Spring MVC下使用Thymeleaf模闆引擎,另外還提供了一個TemplateResolver類來設定通用的模闆引擎(字首、字尾等)。如下:
@Bean
public TemplateResolver templateResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.setTemplateResolver(templateResolver());
return springTemplateEngine;
}
@Bean
public ThymeleafViewResolver thymeleafViewEngine() {
ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
thymeleafViewResolver.setTemplateEngine(templateEngine());
// thymeleafViewResolver.setViewClass(ThymeleafView.class);
return thymeleafViewResolver;
}
3 Spring Boot對Thymeleaf的支援
Spring Boot通過org.springframework.boot.autoconfigure.thymeleaf包對thymeleaf進行自動配置。如圖:

通過ThymeleafAutoConfiguration對內建所需的Bean進行自動配置,包括templateResolver、templateEngine和thymeleafResolver的配置。
通過ThymeleafProperties配置Thymeleaf,再application.properties檔案中以spring.thymeleaf開頭,通過源碼我們可以看出如何設定屬性和預設值:
package org.springframework.boot.autoconfigure.thymeleaf;
import java.nio.charset.Charset;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.MimeType;
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
/**
* 字首設定,Spring Boot預設模闆,放置在classpath:/templates/目錄下
*/
private String prefix = DEFAULT_PREFIX;
/**
* 字尾設定,預設是".html"
*/
private String suffix = DEFAULT_SUFFIX;
/**
* 模闆模式設定,預設是"HTML5"
*/
private String mode = "HTML5";
/**
* 模闆編碼設定,預設是"UTF-8"
*/
private Charset encoding = DEFAULT_ENCODING;
/**
* 模闆媒體類型設定,預設是"text/html"
*/
private MimeType contentType = DEFAULT_CONTENT_TYPE;
/**
* 是否開啟模闆緩存,預設是開啟,開發時請關閉
*/
private boolean cache = true;
/**
* Comma-separated list of view names that can be resolved.
*/
private String[] viewNames;
/**
* Comma-separated list of view names that should be excluded from resolution.
*/
private String[] excludedViewNames;
/**
* Enable MVC Thymeleaf view resolution.
*/
private boolean enabled = true;
...
}
3.1 示例Bean
package com.chenfeng.xiaolyuh.entity;
public class Person {
private String name;
private Integer age;
public Person() {
super();
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3.2 腳本樣式靜态檔案
靜态資源放在/src/main/resources/static目錄下,如圖:
3.3 示範頁面 頁面預設放在/src/main/resources/templates,在該檔案下建立index.html檔案:
<html xmlns:th="http://www.thymeleaf.org"><!-- 通過這個命名空間将靜态視圖轉換成動态的,需要動态的處理的元素前面加上th: -->
<head>
<meta content="text/html;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link th:href="@{bootstrap/css/bootstrap.min.css}" rel="stylesheet"/> <!-- 通過@{}來對靜态資源的引用 -->
<link th:href="@{bootstrap/css/bootstrap-theme.min.css}" rel="stylesheet"/>
</head>
<body>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">通路model</h3>
</div>
<div class="panel-body">
<!-- ${}通路Mode中的屬性(注解要加上th: 字首) -->
<span th:text="${singlePerson.name}"></span>
</div>
</div>
<div th:if="${not #lists.isEmpty(people)}">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">清單</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<!-- 疊代 -->
<li class="list-group-item" th:each="person:${people}">
<span th:text="${person.name}"></span>
<span th:text="${person.age}"></span>
<button class="btn" th:onclick="'getName(\'' + ${person.name} + '\');'">獲得名字</button>
</li>
</ul>
</div>
</div>
</div>
<script th:src="@{jquery-1.10.2.min.js}" type="text/javascript"></script><!-- 2 -->
<script th:src="@{bootstrap/js/bootstrap.min.js}"></script><!-- 2 -->
<script th:inline="javascript"> // th:inline="javascript" 添加到script标簽可以通路mdel中的屬性
var single = [[${singlePerson}]];
console.log(single.name+"/"+single.age)
function getName(name){
console.log(name);
}
</script>
</body>
</html>
3.4 控制層代碼
package com.chenfeng.xiaolyuh;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.chenfeng.xiaolyuh.entity.Person;
import com.chenfeng.xiaolyuh.properties.AuthorSettings;
@SpringBootApplication
@Controller
public class SpringBootStudentApplication {
@RequestMapping("index")
public String index(Model model){
Person single = new Person("aa",11);
List<Person> people = new ArrayList<Person>();
Person p1 = new Person("xx",11);
Person p2 = new Person("yy",22);
Person p3 = new Person("zz",33);
people.add(p1);
people.add(p2);
people.add(p3);
model.addAttribute("singlePerson", single);
model.addAttribute("people", people);
return "index";
}
// 标準的JAVA應用main方法,主要作用作為項目啟動的入口
public static void main(String[] args) {
SpringApplication.run(SpringBootStudentApplication.class, args);
}
}