@[toc] 雖然如今流行先後端分離,可是後端模版在一些關鍵地方仍是很是有用的,例如郵件模版、代碼模版等。固然也不排除一些古老的項目後端依然使用動態模版。javascript
Thymeleaf 簡潔漂亮、容易理解,而且完美支持 HTML5,能夠直接打開靜態頁面,同時不新增標籤,只需加強屬性,這樣也下降了學習成本。css
所以鬆哥今天花點時間和你們仔細分享一下 Thymeleaf。html
1. Thymeleaf 簡介
Thymeleaf 是新一代 Java 模板引擎,它相似於 Velocity、FreeMarker 等傳統 Java 模板引擎,可是與傳統 Java 模板引擎不一樣的是,Thymeleaf 支持 HTML 原型。前端
它既可讓前端工程師在瀏覽器中直接打開查看樣式,也可讓後端工程師結合真實數據查看顯示效果,同時,SpringBoot 提供了 Thymeleaf 自動化配置解決方案,所以在 SpringBoot 中使用 Thymeleaf 很是方便。java
事實上, Thymeleaf 除了展現基本的 HTML ,進行頁面渲染以外,也能夠做爲一個 HTML 片斷進行渲染,例如咱們在作郵件發送時,可使用 Thymeleaf 做爲郵件發送模板。jquery
另外,因爲 Thymeleaf 模板後綴爲 .html
,能夠直接被瀏覽器打開,所以,預覽時很是方便。git
2. 整合 Spring Boot
2.1 基本用法
Spring Boot 中整合 Thymeleaf 很是容易,只須要建立項目時添加 Thymeleaf 便可:web
建立完成後,pom.xml 依賴以下:spring
<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency>
固然,Thymeleaf 不只僅能在 Spring Boot 中使用,也可使用在其餘地方,只不過 Spring Boot 針對 Thymeleaf 提供了一整套的自動化配置方案,這一套配置類的屬性在 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties
中,部分源碼以下:後端
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html"; private boolean checkTemplate = true; private boolean checkTemplateLocation = true; private String prefix = DEFAULT_PREFIX; private String suffix = DEFAULT_SUFFIX; private String mode = "HTML"; private Charset encoding = DEFAULT_ENCODING; private boolean cache = true; //... }
- 首先經過
@ConfigurationProperties
註解,將application.properties
前綴爲spring.thymeleaf
的配置和這個類中的屬性綁定。 - 前三個
static
變量定義了默認的編碼格式、視圖解析器的前綴、後綴等。 - 從前三行配置中,能夠看出來,
Thymeleaf
模板的默認位置在resources/templates
目錄下,默認的後綴是html
。 - 這些配置,若是開發者不本身提供,則使用 默認的,若是本身提供,則在
application.properties
中以spring.thymeleaf
開始相關的配置。
而咱們剛剛提到的,Spring Boot 爲 Thymeleaf 提供的自動化配置類,則是 org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
,部分源碼以下:
@Configuration @EnableConfigurationProperties(ThymeleafProperties.class) @ConditionalOnClass({ TemplateMode.class, SpringTemplateEngine.class }) @AutoConfigureAfter({ WebMvcAutoConfiguration.class, WebFluxAutoConfiguration.class }) public class ThymeleafAutoConfiguration { }
能夠看到,在這個自動化配置類中,首先導入 ThymeleafProperties
,而後 @ConditionalOnClass
註解表示噹噹前系統中存在 TemplateMode
和 SpringTemplateEngine
類時,當前的自動化配置類纔會生效,即只要項目中引入了 Thymeleaf
相關的依賴,這個配置就會生效。
這些默認的配置咱們幾乎不須要作任何更改就能夠直接使用了。若是開發者有特殊需求,則能夠在 application.properties 中配置以 spring.thymeleaf 開頭的屬性便可。
接下來咱們就能夠建立 Controller 了,實際上引入 Thymeleaf 依賴以後,咱們能夠不作任何配置。新建的 IndexController 以下:
@Controller public class IndexController { @GetMapping("/index") public String index(Model model) { List<user> users = new ArrayList<>(); for (int i = 0; i < 10; i++) { User u = new User(); u.setId((long) i); u.setName("javaboy:" + i); u.setAddress("深圳:" + i); users.add(u); } model.addAttribute("users", users); return "index"; } } public class User { private Long id; private String name; private String address; //省略 getter/setter }
在 IndexController
中返回邏輯視圖名+數據,邏輯視圖名爲 index
,意思咱們須要在 resources/templates
目錄下提供一個名爲 index.html
的 Thymeleaf
模板文件。
- 建立 Thymeleaf
<meta charset="UTF-8"> <title>Title</title> <table border="1"> <tbody><tr> <td>編號</td> <td>用戶名</td> <td>地址</td> </tr> <tr th:each="user : ${users}"> <td th:text="${user.id}"></td> <td th:text="${user.name}"></td> <td th:text="${user.address}"></td> </tr> </tbody></table>
在 Thymeleaf
中,經過 th:each
指令來遍歷一個集合,數據的展現經過 th:text
指令來實現,
注意 index.html
最上面引入 thymeleaf
名稱空間(最新版並沒有強制要求)。
配置完成後,就能夠啓動項目了,訪問 /index 接口,就能看到集合中的數據了:
2.2 手動渲染
前面咱們說的是返回一個 Thymeleaf 模板,咱們也能夠手動渲染 Thymeleaf 模板,這個通常在郵件發送時候有用,例如我在 resources/templates 目錄下新建一個郵件模板,以下:
<meta charset="UTF-8"> <title>Title</title> <p>hello 歡迎 <span th:text="${username}"></span>加入 XXX 集團,您的入職信息以下:</p> <table border="1"> <tbody><tr> <td>職位</td> <td th:text="${position}"></td> </tr> <tr> <td>薪水</td> <td th:text="${salary}"></td> </tr> </tbody></table> <img src="https://oscimg.oschina.net/oscnet/javaboy.jpg" alt="">
這一個 HTML 模板中,有幾個變量,咱們要將這個 HTML 模板渲染成一個 String 字符串,再把這個字符串經過郵件發送出去,那麼如何手動渲染呢?
@Autowired TemplateEngine templateEngine; @Test public void test1() throws MessagingException { Context context = new Context(); context.setVariable("username", "javaboy"); context.setVariable("position", "Java工程師"); context.setVariable("salary", 99999); String mail = templateEngine.process("mail", context); //省略郵件發送 }
- 渲染時,咱們須要首先注入一個 TemplateEngine 對象,這個對象就是在 Thymeleaf 的自動化配置類中配置的(即當咱們引入 Thymeleaf 的依賴以後,這個實例就有了)。
- 而後構造一個 Context 對象用來存放變量。
- 調用 process 方法進行渲染,該方法的返回值就是渲染後的 HTML 字符串,而後咱們將這個字符串發送出去。
3. Thymeleaf 細節
前面兩個案例讓小夥伴們大體上理解了在 Spring Boot 中要如何使用 Thymeleaf,接下來,鬆哥將詳細介紹 Thymeleaf 自己的一些具體用法。
3.1 標準表達式語法
3.1.1 簡單表達式
${...}
直接使用 th:xx = "${}"
獲取對象屬性。這個在前面的案例中已經演示過了,再也不贅述。
*{...}
能夠像 ${...}
同樣使用,也能夠經過 th:object
獲取對象,而後使用 th:xx = "*{}"
獲取對象屬性,這種簡寫風格極爲清爽,推薦你們在實際項目中使用。
<table border="1" th:object="${user}"> <tbody><tr> <td>用戶名</td> <td th:text="*{username}"></td> </tr> <tr> <td>地址</td> <td th:text="*{address}"></td> </tr> </tbody></table>
#{...}
一般的國際化屬性:#{...}
用於獲取國際化語言翻譯值。
在 resources 目錄下新建兩個文件:messages.properties 和 messages_zh_CN.properties,內容以下:
messages.properties:
message = javaboy
messages_zh_CN.properties:
message = 江南一點雨
而後在 thymeleaf 中引用 message,系統會根據瀏覽器的語言環境顯示不一樣的值:
<div th:text="#{message}"></div>
@{...}
- 引用絕對 URL:
<script type="text/javascript" th:src="@{http://localhost:8080/hello.js}"></script>
等價於:
<script type="text/javascript" src="http://localhost:8080/hello.js"></script>
- 上下文相關的 URL:
首先在 application.properties 中配置 Spring Boot 的上下文,以便於測試:
server.servlet.context-path=/myapp
引用路徑:
<script type="text/javascript" th:src="@{/hello.js}"></script>
等價於:
<script type="text/javascript" src="/myapp/hello.js"></script>
- 相對 URL:
這個相對是指相對於服務器的 URL,例如以下引用:
<script type="text/javascript" th:src="@{~/hello.js}"></script>
等價於:
<script type="text/javascript" src="/hello.js"></script>
應用程序的上下文 /myapp 將被忽略。
- 協議相對 URL:
<script type="text/javascript" th:src="@{//localhost:8080/hello.js}"></script>
等價於:
<script type="text/javascript" src="//localhost:8080/hello.js"></script>
- 帶參數的 URL:
<script type="text/javascript" th:src="@{//localhost:8080/hello.js(name='javaboy',age=99)}"></script>
等價於:
<script type="text/javascript" th:src="//localhost:8080/hello.js?name=javaboy&age=99"></script>
~{...}
片斷表達式是 Thymeleaf 的特點之一,細粒度能夠達到標籤級別,這是 JSP 沒法作到的。片斷表達式擁有三種語法:
~{ viewName }
:表示引入完整頁面~{ viewName ::selector}
:表示在指定頁面尋找片斷,其中 selector 可爲片斷名、jquery選擇器等~{ ::selector}
: 表示在當前頁尋找
舉個簡單例子。
在 resources/templates 目錄下新建 my_fragment.html 文件,內容以下:
<div th:fragment="javaboy_link"><a href="http://www.javaboy.org">www.javaboy</a></div> <div th:fragment="itboyhub_link"><a href="http://www.itboyhub.com">www.itboyhub.com</a></div>
這裏有兩個 div,經過 th:fragment 來定義片斷,兩個 div 分別具備不一樣的名字。
而後在另一個頁面中引用該片斷:
<table border="1" th:object="${user}" th:fragment="aaa"> <tbody><tr> <td>用戶名</td> <td th:text="*{username}"></td> </tr> <tr> <td>地址</td> <td th:text="*{address}"></td> </tr> </tbody></table> <hr> <div th:replace="my_fragment.html"></div> <hr> <div th:replace="~{my_fragment.html::javaboy_link}"></div> <hr> <div th:replace="~{::aaa}"></div>
經過 th:replace 來引用片斷。第一個表示引用完整的 my_fragment.html
頁面;第二個表示引用 my_fragment.html
中的名爲 javaboy_link
的片斷;第三個表示引用當前頁面名爲 aaa 的片斷,也就是上面那個 table。
3.1.2 字面量
這些是一些能夠直接寫在表達式中的字符,主要有以下幾種:
- 文本字面量: 'one text', 'Another one!',…
- 數字字面量: 0, 34, 3.0, 12.3,…
- 布爾字面量: true, false
- Null字面量: null
- 字面量標記:one, sometext, main,…
案例:
<div th:text="'這是 文本字面量(有空格)'"></div> <div th:text="javaboy"></div> <div th:text="99"></div> <div th:text="true"></div>
若是文本是英文,而且不包含空格、逗號等字符,能夠不用加單引號。
3.1.3 文本運算
文本可使用 +
進行拼接。
<div th:text="'hello '+'javaboy'"></div> <div th:text="'hello '+${user.username}"></div>
若是字符串中包含變量,也可使用另外一種簡單的方式,叫作字面量置換,用 |
代替 '...' + '...'
,以下:
<div th:text="|hello ${user.username}|"></div> <div th:text="'hello '+${user.username}+' '+|Go ${user.address}|"></div>
3.1.4 算術運算
算術運算有:+
, -
, *
, /
和 %
。
<div th:with="age=(99*99/99+99-1)"> <div th:text="${age}"></div> </div>
th:with 定義了一個局部變量 age,在其所在的 div 中可使用該局部變量。
3.1.5 布爾運算
- 二元運算符:and, or
- 布爾非(一元運算符):!, not
案例:
<div th:with="age=(99*99/99+99-1)"> <div th:text="9 eq 9 or 8 ne 8"></div> <div th:text="!(9 eq 9 or 8 ne 8)"></div> <div th:text="not(9 eq 9 or 8 ne 8)"></div> </div>
3.1.6 比較和相等
表達式裏的值可使用 >
, <
, >=
和 <=
符號比較。==
和 !=
運算符用於檢查相等(或者不相等)。注意 XML
規定 <
和 >
標籤不能用於屬性值,因此應當把它們轉義爲 <
和 >
。
若是不想轉義,也可使用別名:gt (>);lt (<);ge (>=);le (<=);not (!)。還有 eq (==), neq/ne (!=)。
舉例:
<div th:with="age=(99*99/99+99-1)"> <div th:text="${age} eq 197"></div> <div th:text="${age} ne 197"></div> <div th:text="${age} ge 197"></div> <div th:text="${age} gt 197"></div> <div th:text="${age} le 197"></div> <div th:text="${age} lt 197"></div> </div>
3.1.7 條件運算符
相似於咱們 Java 中的三目運算符。
<div th:with="age=(99*99/99+99-1)"> <div th:text="(${age} ne 197)?'yes':'no'"></div> </div>
其中,: 後面的部分能夠省略,若是省略了,又同時計算結果爲 false 時,將返回 null。
3.1.8 內置對象
基本內置對象:
- #ctx:上下文對象。
- #vars: 上下文變量。
- #locale:上下文區域設置。
- #request:(僅在 Web 上下文中)HttpServletRequest 對象。
- #response:(僅在 Web 上下文中)HttpServletResponse 對象。
- #session:(僅在 Web 上下文中)HttpSession 對象。
- #servletContext:(僅在 Web 上下文中)ServletContext 對象。
在頁面能夠訪問到上面這些內置對象,舉個簡單例子:
<div th:text="${#session.getAttribute("name")}"></div>
實用內置對象:
- #execInfo:有關正在處理的模板的信息。
- #messages:在變量表達式中獲取外部化消息的方法,與使用#{...}語法得到的方式相同。
- #uris:轉義URL / URI部分的方法
- #conversions:執行配置的轉換服務(若是有)的方法。
- #dates:java.util.Date對象的方法:格式化,組件提取等
- #calendars:相似於#dates可是java.util.Calendar對象。
- #numbers:用於格式化數字對象的方法。
- #strings:String對象的方法:contains,startsWith,prepending / appending等
- #objects:通常對象的方法。
- #bools:布爾評估的方法。
- #arrays:數組方法。
- #lists:列表的方法。
- #sets:集合的方法。
- #maps:地圖方法。
- #aggregates:在數組或集合上建立聚合的方法。
- #ids:處理可能重複的id屬性的方法(例如,做爲迭代的結果)。
這是一些內置對象以及工具方法,使用方式也都比較容易,若是使用的是 IntelliJ IDEA,都會自動提示對象中的方法,很方便。
舉例:
<div th:text="${#execInfo.getProcessedTemplateName()}"></div> <div th:text="${#arrays.length(#request.getAttribute('names'))}"></div>
3.2 設置屬性值
這個是給 HTML 元素設置屬性值。能夠一次設置多個,多個之間用 ,
分隔開。
例如:
<img th:attr="src=@{/1.png},title=${user.username},alt=${user.username}" src="">
會被渲染成:
<img src="/myapp/1.png" title="javaboy" alt="javaboy">
固然這種設置方法不太美觀,可讀性也很差。Thymeleaf 還支持在每個原生的 HTML 屬性前加上 th: 前綴的方式來使用動態值,像下面這樣:
<img th:src="@{/1.png}" th:alt="${user.username}" th:title="${user.username}" src="">
這種寫法看起來更清晰一些,渲染效果和前面一致。
上面案例中的 alt 和 title 則是兩個特殊的屬性,能夠一次性設置,像下面這樣:
<img th:src="@{/1.png}" th:alt-title="${user.username}" src="">
這個等價於前文的設置。
3.3 遍歷
數組/集合/Map/Enumeration/Iterator 等的遍歷也算是一個很是常見的需求,Thymeleaf 中經過 th:each 來實現遍歷,像下面這樣:
<table border="1"> <tbody><tr th:each="u : ${users}"> <td th:text="${u.username}"></td> <td th:text="${u.address}"></td> </tr> </tbody></table>
users 是要遍歷的集合/數組,u 則是集合中的單個元素。
遍歷的時候,咱們可能須要獲取遍歷的狀態,Thymeleaf 也對此提供了支持:
- index:當前的遍歷索引,從0開始。
- count:當前的遍歷索引,從1開始。
- size:被遍歷變量裏的元素數量。
- current:每次遍歷的遍歷變量。
- even/odd:當前的遍歷是偶數次仍是奇數次。
- first:當前是否爲首次遍歷。
- last:當前是否爲最後一次遍歷。
u 後面的 state 表示遍歷狀態,經過遍歷狀態能夠引用上面的屬性。
<table border="1"> <tbody><tr th:each="u,state : ${users}"> <td th:text="${u.username}"></td> <td th:text="${u.address}"></td> <td th:text="${state.index}"></td> <td th:text="${state.count}"></td> <td th:text="${state.size}"></td> <td th:text="${state.current}"></td> <td th:text="${state.even}"></td> <td th:text="${state.odd}"></td> <td th:text="${state.first}"></td> <td th:text="${state.last}"></td> </tr> </tbody></table>
3.4 分支語句
只顯示奇數次的遍歷,可使用 th:if,以下:
<table border="1"> <tbody><tr th:each="u,state : ${users}" th:if="${state.odd}"> <td th:text="${u.username}"></td> <td th:text="${u.address}"></td> <td th:text="${state.index}"></td> <td th:text="${state.count}"></td> <td th:text="${state.size}"></td> <td th:text="${state.current}"></td> <td th:text="${state.even}"></td> <td th:text="${state.odd}"></td> <td th:text="${state.first}"></td> <td th:text="${state.last}"></td> </tr> </tbody></table>
th:if 不只僅只接受布爾值,也接受其餘類型的值,例如以下值都會斷定爲 true:
- 若是值是布爾值,而且爲 true。
- 若是值是數字,而且不爲 0。
- 若是值是字符,而且不爲 0。
- 若是值是字符串,而且不爲 「false」, 「off」 或者 「no」。
- 若是值不是布爾值,數字,字符或者字符串。
可是若是值爲 null,th:if 會求值爲 false。
th:unless 的斷定條件則與 th:if 徹底相反。
<table border="1"> <tbody><tr th:each="u,state : ${users}" th:unless="${state.odd}"> <td th:text="${u.username}"></td> <td th:text="${u.address}"></td> <td th:text="${state.index}"></td> <td th:text="${state.count}"></td> <td th:text="${state.size}"></td> <td th:text="${state.current}"></td> <td th:text="${state.even}"></td> <td th:text="${state.odd}"></td> <td th:text="${state.first}"></td> <td th:text="${state.last}"></td> </tr> </tbody></table>
這個顯示效果則與上面的徹底相反。
當可能性比較多的時候,也可使用 switch:
<table border="1"> <tbody><tr th:each="u,state : ${users}"> <td th:text="${u.username}"></td> <td th:text="${u.address}"></td> <td th:text="${state.index}"></td> <td th:text="${state.count}"></td> <td th:text="${state.size}"></td> <td th:text="${state.current}"></td> <td th:text="${state.even}"></td> <td th:text="${state.odd}"></td> <td th:text="${state.first}"></td> <td th:text="${state.last}"></td> <td th:switch="${state.odd}"> <span th:case="true">odd</span> <span th:case="*">even</span> </td> </tr> </tbody></table>
th:case="*"
則表示默認選項。
3.5 本地變量
這個咱們前面已經涉及到了,使用 th:with 能夠定義一個本地變量。
3.6 內聯
咱們可使用屬性將數據放入頁面模版中,可是不少時候,內聯的方式看起來更加直觀一些,像下面這樣:
<div>hello [[${user.username}]]</div>
用內聯的方式去作拼接也顯得更加天然。
[[...]]
對應於 th:text (結果會是轉義的 HTML),[(...)]
對應於 th:utext,它不會執行任何的 HTML 轉義。
像下面這樣:
<div th:with="str='hello <strong>javaboy</strong>'"> <div>[[${str}]]</div> <div>[(${str})]</div> </div>
最終的顯示效果以下:
不過內聯方式有一個問題。咱們使用 Thymeleaf 的一大優點在於不用動態渲染就能夠直接在瀏覽器中看到顯示效果,當咱們使用屬性配置的時候確實是這樣,可是若是咱們使用內聯的方式,各類表達式就會直接展現在靜態網頁中。
也能夠在 js 或者 css 中使用內聯,以 js 爲例,使用方式以下:
<script th:inline="javascript"> var username=[[${user.username}]] console.log(username) </script>
js 中須要經過 th:inline="javascript"
開啓內聯。
4. 小結
好啦,Thymeleaf 跟你們也介紹的差很少了,應付平常的工做應該是能夠了。對 Thymeleaf 感興趣的小夥伴,也能夠看看它的官方文檔: https://www.thymeleaf.org。
最後,鬆哥還蒐集了 50+ 個項目需求文檔,想作個項目練練手的小夥伴不妨看看哦~
需求文檔地址:https://gitee.com/lenve/javadoc</user>