springboot中Thymeleaf的使用

一. 什么是Thymeleaf

Thymeleaf是面向Web和獨立環境的現代服務器端Java模板引擎。
Thymeleaf的主要目標是為您的開發工作流程帶來優雅的自然模板 - 可以正確顯示在瀏覽器中的HTML,也可以作為靜態原型工作,從而在開發團隊中進行更強大的協作。
隨著Spring框架的模塊,與您最喜歡的工具的集成,以及插入自己的功能的能力,Thymeleaf是現代HTML5 JVM Web開發的理想選擇,盡管它可以做的更多。

好吧,我承認剛才那段是Thymeleaf官方的說明,我只不過機翻了一下。下面咱們說點人話。Thymeleaf就是jsp的高端升級版。

二. 什么情況適合使用Thymeleaf

Thymeleaf顯然是一個開發頁面的技術,現在各種前端技術層出不窮,比如現在主流的Vue、React、AngularJS等。很多人可能會要問,這個Thymeleaf相對于這些前端框架到底有啥優勢。
其實,Thymeleaf跟那些前端框架根本不是一個類型的東西,也沒有啥可比性。

Thymeleaf和老牌的jsp屬于非前后分離的思路來開發的。后端通過數據渲染html的模板,渲染后模板就是個完整的html頁面,將頁面返回給請求方。

主流的前端框架是基于前后端分離的思路來開發的,前端頁面通過ajax來調用后端的rest接口來獲取數據,再通過js進行渲染頁面(不管什么前端技術其實都是對js進行了封裝,js依然是底層核心)。

使用前后分離主要有下面幾個好處

  1. 因為每次請求服務器獲取的數據從整個頁面變成了僅僅是核心數據,加載速度明顯提升。
  2. 前端人員和后端人員可以互相獨立開發,最后在通過接口聯調即可。以前是不分前端工程師、后端工程師的,現在前后分離后,才出現了這樣的分類。而且現在前端技術也越來越先進。前后分離以后可以方便兩條技術路線的人員各自鉆研自己的技術。
  3. 前端頁面脫離后端服務器后,可以和后端分開部署。這時就可以對前端頁面的服務器進行一些專門的網絡優化進一步提高訪問速度。
  4. 后端只需要一套rest接口就可以同時服務于電腦頁面、IOS客戶端、安卓客戶端。甚至現在還有些前端技術可以直接把前端頁面打包成IOS、安卓的客戶端。
  5. ......

說了這么多前后分離的好處,你可能就要問了,那我們為什么還要用那個看起來那么low的模板引擎呢?

為了速度。前后分離方式,前端頁面通過ajax來調用后端的rest接口來獲取數據,再通過js進行渲染頁面。獲取數據和通過js渲染頁面的代碼,很多時候比頁面本身要多的多,而且通過js來操作dom進行渲染,稍微復雜些的頁面往往就會把渲染邏輯搞的錯綜復雜。相信從jsp時代一路走到現在的老程序員都深知工作量是成倍的往上翻。

固然剛才列舉了前后分離的種種好處,但這些好處大多數都是集中在app開發上,其他某些場景下這些好處并不明顯。最典型的一個場景就是管理后臺。管理后臺往往對頁面的花哨性要求不高,并發量也不大,而且功能往往還不少。這種場景下,前后分離技術上導致的工作量大幅度增加,人員上分離導致額外的聯調成本都成了不少的負擔。Thymeleaf作為模板引擎這時候優勢就非常大。只需要在html原型的頁面上稍微加幾個標簽,即可完成渲染。而且加上的標簽并不影響原型頁面直接通過html打開。

說了這么多,總結一下,Thymeleaf是一個供后端人員使用的,為快速開發頁面而生的Java模板引擎。

三. 如何在Springboot中引入Thymeleaf

Thymeleaf作為spring官方推薦的模板引擎,在spring體系中使用異常方便。這里以gradle構建的項目為例來說明。
首先,你要先修改build.gradle引入Springboot對Thymeleaf提供的依賴包。在dependencies中增加如下配置。

compile('org.springframework.boot:spring-boot-starter-thymeleaf')

等待gradle幫你自己下載完依賴包后,你可以看到引入的Thymeleaf的版本。

Thymeleaf依賴包

嗯?springboot1.5.7默認引用的Thymeleaf依賴包居然還是2.1.5版本。最新的Thymeleaf不是已經更新3.x版本了么。如果我想使用最新版的Thymeleaf要怎么辦呢。
在build.gradle文件中,buildscript下增加配置,完整的配置如下圖

ext['thymeleaf.version'] = '3.0.7.RELEASE'
ext['thymeleaf-layout-dialect.version'] = '2.2.2'
完整gradle配置

等待gradle下載完成,你可以看到引入的Thymeleaf已經是最新版本了。

四. 快速入門

接下來要在項目中使用Thymeleaf了,這里用一個簡單的單表查詢來舉個栗子。
一般來說,開發一個需要渲染數據的頁面,分為三個步驟。

  1. 開發靜態頁面,即常說的模型。
  2. 獲取數據。
  3. 使用數據對靜態頁面進行渲染。

這里我們先做第一個步驟,開發靜態頁面。為了簡單,就不做任何css了,下面是頁面的源碼。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>標題</title>
</head>
<body>
  <strong>標題</strong>
  <form action="list.html" method="post">
    <input type="hidden" name="pageNumber">
    用戶名:<input type="text" name="username">
    <br/>
    姓名:<input type="text" name="name">
    <br/>
    <button type="submit">提交</button> <button type="reset">重置</button>
  </form>
  <table>
    <thead>
      <tr>
        <th class="am-hide-sm-only">id</th>
        <th>用戶名</th>
        <th>姓名</th>
        <th class="am-hide-sm-only">電話</th>
        <th class="am-hide-sm-only">郵箱</th>
        <th class="am-hide-sm-only">是否可用</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>id</td>
        <td>用戶名</td>
        <td>姓名</td>
        <td>電話</td>
        <td>郵箱</td>
        <td>
          <span>可用</span><span>不可用</span>
        </td>
        <td><button>修改</button><button>刪除</button></td>
      </tr>
    </tbody>
  </table>
</body>
</html>

直接使用瀏覽器打開該頁面,長成這樣。

靜態頁面

現在有了靜態頁面,該獲取數據了。下面是controller層的代碼。

/**
 * 用戶管理
 */
@Controller
@RequestMapping("/users")
public class UserController
{
    @Autowired
    private UserRepository userRepository;

    @Value("${pageSize}")
    private Integer pageSize;

    /**
     * 分頁查詢信息
     */
    @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
    public String list(Model model, User user, @RequestParam(value = "pageNumber", required = false, defaultValue = "0") Integer pageNumber)
    {
        ExampleMatcher matcher = ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
        PageRequest pageRequest = new PageRequest(pageNumber, pageSize);

        Page<User> page = userRepository.findAll(Example.of(user, matcher), pageRequest);

        //分頁查詢數據
        model.addAttribute("page", page);
        //查詢條件
        model.addAttribute("user", user);
        //頁面標題
        model.addAttribute("title", "用戶管理");
        //轉到待渲染模板,所有模板都在templates文件夾下,users/list指templates文件夾下的users文件夾下的list.html頁面。
        return "users/list";
    }
}

這里使用spring-data-jpa從數據庫里查詢到了記錄并和查詢條件、頁面標題一起轉到待渲染的模板。這里,我們將剛才的靜態頁面文件復制到對應的位置。如下圖。

Thymeleaf模板擺放位置

下面,我們要對該文件進行適當的改造,使之成為一個Thymeleaf模板文件。先貼上改造后的文件。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title th:text="${title}">標題</title>
  <meta charset="UTF-8">
</head>
<body>
  <strong th:text="${title}">標題</strong>
  <form th:action="@{/users}" th:object="${user}" method="post">
    <input type="hidden" name="pageNumber" th:value="${page.number}">
    用戶名:<input type="text" name="username" th:value="*{username}">
    <br/>
    姓名:<input type="text" name="name" th:value="*{name}">
    <br/>
    <button type="submit">提交</button> <button type="reset">重置</button>
  </form>
  <table>
    <thead>
      <tr>
        <th class="am-hide-sm-only">id</th>
        <th>用戶名</th>
        <th>姓名</th>
        <th class="am-hide-sm-only">電話</th>
        <th class="am-hide-sm-only">郵箱</th>
        <th class="am-hide-sm-only">是否可用</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
      <tr th:each="user : ${page.content}">
        <td th:text="${user.id}">id</td>
        <td th:text="${user.username}">用戶名</td>
        <td th:text="${user.name}">姓名</td>
        <td th:text="${user.phone}">電話</td>
        <td th:text="${user.email}">郵箱</td>
        <td th:switch="${user.enabled}">
          <span th:case="true">可用</span><span th:case="false">不可用</span>
        </td>
        <td><button>修改</button><button>刪除</button></td>
      </tr>
    </tbody>
  </table>
</body>
</html>

可以看到,我們對html代碼的結構絲毫未改動,只是在一些標簽里面添加了th:xxx="yyy"的屬性。
我們重新使用瀏覽器打開Thymeleaf改造過的html文件。發現雖然我們添加了那么多th:xxx="yyy"的標簽,但是,頁面居然和之前一模一樣。

Thymeleaf改造后頁面

下面我們啟動服務,通過controller定義的那個url來訪問渲染后的頁面。

渲染后的頁面

同一個文件,瀏覽器直接打開就是原型,服務器渲染后打開就是真實的功能頁面。
這里我們就可以看出Thymeleaf的一個核心功能,就是將其邏輯注入到模板文件中,不會影響模板被用作設計原型。做到了原型即頁面。

Thymeleaf的核心語法就是th:xxx="yyy",即設置html標簽中xxx屬性的值為yyy對應的值。

五. 設置屬性

我們先來說一說th:xxx的部分,即設置屬性。

Thymeleaf的核心功能就是通過在html標簽里面追加屬性??梢栽O置的屬性非常多,詳細的可以參考 Thymeleaf所有屬性的還沒發文檔。這里我們就挑選些常用的舉幾個例子,其他大家可以舉一反三推斷出用法。

  • th:object="yyy" 將對象作為一個范圍內可用的變量。一般和選擇表達式*{zzz}配合使用,選擇表達式后面會講到。

  • th:text="yyy" 這個屬性可以添加到幾乎所有分為頭尾兩部分<></>的html標簽中,如<title></title>、<td><td/>等。th:text="yyy"的作用是把表達式yyy對應的值添加到標簽的中間。
    <td th:text="user">用戶名</td>渲染后就是<td>user<td/>

  • th:value="${title}" 這個屬性一般和<input />標簽搭配使用,用來設置<input />標簽的value值。
    <input th:value="username" />渲染后就是<input value="username" />。

六. 表達式

下面我們再來對這些th:xxx="yyy""yyy"的部分進行講解。這個yyy我們一般稱之為表達式。
Thymeleaf里面表達式主要有以下幾種。

  • ${yyy} 變量表達式,用來獲取上下文對象里面的值(controller返回的model)。還是以上面的例子來說明,如果我想要取到page對象中的number屬性,使用${page.number}即可。
變量表達式
  • #{yyy} 消息表達式,根據消息的key來獲取消息內容。一般是用來做國際化用的。
  • *{yyy} 選擇表達式,跟變量表達式用法差不多,但變量表達式是獲取上下文里的對象,選擇表達式是獲取一個選擇的對象。
    選擇表達式一般和th:object=標簽配合使用,還是以上面的例子來說明。
    先用th:object="${user}"選擇了上下文中的user對象,下面想使用user對象的username屬性時,直接使用*{username}就可以了。
    你可能想要問,我直接使用${user.username}不是一樣可以找到user對象的username屬性么,為什么還要再搞個選擇表達式?
    因為${user.username}是先從下上文找到user,對象,再從user對象里找到username屬性;而*{username}是直接從user對象里找到username屬性。當需要從一個對象里獲取很多屬性的時候,使用選擇表達式可以提高效率。
選擇表達式
  • @{yyy} 鏈接表達式 設置超鏈接時用的表達式,一般和th:actionth:href配合使用。
鏈接表達式
  • ~{page :: fragment} 分段表達式,主要用作公共模塊的復用,一般和th:insertth:replace搭配使用。以后講模塊復用的時候再細說。

  • yyy 文字??梢詾樽址?、數字、布爾、null。如<td th:text="user">用戶名</td>渲染后為<td>user</td>

  • _ 無操作。下劃線是thymeleaf表達式的特殊字符,如果表達式就一個下劃線,則什么也不做。例如<td th:text="_">用戶名</td>渲染后依然是<td>用戶名</td>。

七. 迭代器

我們在渲染頁面時,經常需要對一個list進行循環處理,最典型的場景就是使用表格展示多條數據。這時,就需要使用到thymeleaf的迭代器th:each。

<tr th:each="user : ${users}">
  <td th:text="${user.id}">id</td>
  <td th:text="${user.username}">用戶名</td>
</tr>

在這個例子中,users是一個list,通過迭代器th:each對其進行遍歷,每次迭代獲取到的對象為user。在th:each屬性的對應的標簽之間<tr th:each="user : ${users}">...</tr>,為user對象的有效范圍。

有時候,我們還想要知道迭代器的一些狀態屬性,如總數,當前索引等??梢酝ㄟ^如下方法獲取。

<tr th:each="user,stat : ${users}">
<td th:text="${stat.index}">index</td>  
<td th:text="${user.id}">id</td>
  <td th:text="${user.username}">用戶名</td>
</tr>

th:each=""的第二個變量stat,就是迭代器的狀態變量,從這個狀態變量里面可以獲取到很多我們想要的屬性,主要有下面這些。

  • stat.index 當前對象在list中的索引。從0開始。
  • stat.countindex差不多,也是當前對象在list中的索引,不過是從1開始。
  • stat.size 迭代器中元素的總數。
  • stat.current 當前迭代的對象。
  • stat.even 當前迭代的索引是否是奇數,索引指stat.index
  • stat.odd 當前迭代的索引是否是偶數,索引指stat.index
  • stat.first 當前迭代的對象是否是迭代器中的第一個。
  • stat.last 當前迭代的對象是否是迭代器中的最后一個。

八. 條件語句

th:if="boolean" th:if的表達式需為boolean值。如果為true,則標簽顯示,如果為false,則標簽不顯示。
th:unless="boolean" th:unlessth:if相反,表達式也需為boolean值。如果為true,則標簽不顯示,如果為false,則標簽顯示。

<span th:if="${stat.odd}">偶</span>
<span th:unless="${stat.odd}">奇</span>
條件判斷

th:swtich 一般和 th:case結合使用 。和java語言中的swtich case語法用法類似。

<td th:switch="${user.enabled}">
  <span th:case="true">可用</span><span th:case="false">不可用</span>
</td>
switch

九. 工具類

Thymeleaf提供了一些工具類,這里舉個簡單的例子展示下用法,其他詳細的可以查看Thymeleaf工具類官方文檔

#lists 數組工具類

總共有<span th:text="${#lists.size(page.content)}">1</span>條記錄
#lists
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • Thymeleaf簡介 前面的例子我們使用的視圖技術主要是JSP。JSP的優點是它是Java EE容器的一部分,幾...
    樂百川閱讀 9,027評論 2 56
  • Thymeleaf 簡易教程 本文源碼可以在這里下載: https://github.com/codergege/...
    codergege閱讀 50,716評論 4 30
  • Thymeleaf 一直以來都是個使用小眾的模板引擎,在2.0以前,最為人吐槽的是性能跌到無底線。甚至朋友的項目因...
    閑大賦閱讀 7,159評論 1 12
  • 某人,你一定想不到的是風輕云淡是最撕心裂肺。我也沒想到。 我應該是太遲鈍了,像很多人一樣,最不了解的還是自己。有人...
    智商堪憂閱讀 123評論 0 0
  • 一朵芙蕖,開過尚盈盈,芊芊淑女,旗袍韻娉婷,炎炎夏日一清涼,您是否有興趣來見證這場美麗邂逅?麒麟水鄉 2017年“...
    麒麟區農旅投王倩閱讀 191評論 0 0