Spring MVC:從 JSP 與 Tiles 到 Thymeleaf

工程 | Michael Isvy | 2012年10月30日 | ...

談到視圖層,Spring @MVC 提供了多種選擇。在本文中,我們將首先討論您在過去幾年中最常使用的視圖層方式:JSP。我們將看到使用它們的不好和更好的方法(純 JSP、帶有自訂標籤的 JSPApache Tiles)。

接著,我們將討論一個名為 Thymeleaf 的新專案,您可以將其用作 JSP 的替代方法。

像往常一樣,您可以在 github 上的對應應用程式中找到本文討論的所有程式碼範例。

純 JSP

讓我們從下面的程式碼範例開始

<html …> <body>
 <div style="padding-top: 50px;">
   <jsp:include page="../menu.jspx"/>
   <c:choose>
     <c:when test="${empty users}">
       Table is empty.
     </c:when>
     <c:otherwise>
      <table>
       <thead>
         <tr>
          <th> First Name </th>
          <th> Last name </th>
         </tr>
        </thead>
        <tbody>
        <c:forEach var="user" items="${users}">
        <tr>
          <td> <c:out value="${user.firstName}"/> </td>
          <td> <c:out value="${user.lastName}"/> </td>
        </tr>
        </c:forEach>
       </tbody>
     </table>
    </c:otherwise>
   </c:choose>
   <jsp:include page="../footer.jspx"/>
  </div>
 </body>
</html>

這個程式碼範例並非完全「最先進」,因為它可能在 8 年前就以完全相同的方式編寫出來。這是否意味著它已過時? 讓我們討論一些可能的限制。

  1. 版面配置

我們使用 <jsp:include /> 來包含 JSP 片段(頁首和頁尾)。顯然,擁有 <jsp:include /> 是好的,因為它可以防止您進行大量複製/貼上。但是,如果您有數百個 JSP 檔案,您會發現自己將這些 <jsp:include /> 標籤複製/貼到所有 JSP 中。最好將所有版面配置資訊外部化到專用檔案中。

  1. 冗長

我們的使用者頁面相當小,因為它僅顯示元素列表。我們已經有 50 行程式碼(上面的程式碼範例已略微 縮減)。您可以想像,如果我們必須顯示大量內容,它會有多大。

  1. HTML/CSS 相容性

此頁面不符合 HTML/CSS 標準。假設網頁設計師已對其進行原型設計,您將必須完全重寫它才能使用侵入性的 JSP 語法。我們將在討論 ThymeLeaf 時再回到這一點。

JSP 自訂標籤

自訂標籤是 Java EE 的一部分。它們允許您外部化 JSP 的重複部分,而無需編寫任何 Java 程式碼。您可以改為建立專用的 .tagx 檔案。

這是一個範例


<jsp:directive.attribute name="title" required="true" rtexprvalue="true" />
<body>
 <div style="padding-top: 50px;">
  <jsp:include page="/WEB-INF/view/jsp/menu.jspx"/>
  <jsp:doBody />
  <jsp:include page="/WEB-INF/view/jsp/footer.jspx"/>
 </div>
 </body>

在範例應用程式中,此檔案名為 mainLayout.tagx。它在我的檔案系統中。

上面範例中最重要的指令是 <jsp:doBody />。當範本被處理時,<jsp:doBody /> 會被來自「main」JSP 的內容替換。

在每個 JSP 內部,我可以調用先前建立的標籤。

如下所示,我們將 custom 命名空間與我們的 tags 資料夾關聯起來。然後,我們可以使用名為 mainLayout 的標籤。


<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:jsp="http://java.sun.com/JSP/Page"
 xmlns:custom="urn:jsptagdir:/WEB-INF/tags">
<custom:mainLayout title="${title}/">
 <c:choose>
…
 </c:choose>
</custom:mainLayout>

注意: 如您在上面的程式碼範例中所見,每個 JSP 都應指定其使用的版面配置。如果我有幾個版面配置,並且想要將幾個 JSP 從 mainLayout 遷移到 customLayout,則需要編輯每個 JSP 檔案並手動更改版面配置。我們稍後在討論 Tiles 時再回到這一點。

自訂標籤可以為您做的遠遠不止外部化版面配置部分。為了說明這一點,我建立了一個 simpleTable 標籤,這樣我就不必處理 <thead> 和 <tbody> 標籤。當表格為空時,它也會顯示一條訊息。我的 JSP 檔案現在看起來像這樣


<custom:mainLayout title="${title}/">
 <custom:simpleTable collection="${users}" headerLabels="First Name, Last Name">
   <c:forEach var="user" items="${users}">
     <tr>
       <td> <c:out value="${user.firstName}"/> </td>
       <td> <c:out value="${user.lastName}"/> </td>
     </tr>
   </c:forEach>
  </custom:simpleTable>
</custom:mainLayout>

您可以瀏覽 simpleTable.tagx 以取得完整範例。

 

注意: 我還應該提及一個由 Thibault Duchateau 建立的新專案,名為 Datatable4J。它在 jquery-datatables 之上提供了一組標籤,因此允許建立 AJAX 樣式的資料表,而無需自己編寫 Javascript。文件相當不錯,並且這個專案正在積極開發中。

 

優點與缺點

標準標籤有很多好處
  • 我可以使用它們做更多的事情,而不僅僅是外部化版面配置資訊。最終,它們可以輕鬆地使您的 JSP 比其他情況下小 5 倍。
  • Eclipse/STS 與自訂標籤配合良好,因此您可以使用 CTRL+space 進行自動完成。
缺點是
  • 文件不是最好的。
  • 儘管自訂標籤已經非常簡潔,但我回想不起過去幾年對它們進行過任何改進。
  • 使用自訂標籤意味著您正在 Web 容器內使用 JSP 引擎。然後,您可以預期根據您使用的 Web 容器(Apache Tomcat、IBM Websphere、Oracle Weblogic…)會有一些微小的差異。
  • 單元測試您的視圖層也更困難。如果您對此主題感興趣,可以查看 Spring MVC Test framework

使用 Apache Tiles 外部化 JSP 的版面配置

Apache Tiles 在十年前就因作為 Struts 1 附帶的版面配置外掛程式而聞名。現在它是一個獨立的框架,並且與 Spring MVC 整合良好。

首先,您應該宣告適當的 Spring 配置


<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver"/>
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
 <property name="definitions">
 <list>
 <value>/WEB-INF/view/jsp/tiles.xml</value>
 </list>
 </property>
</bean>

在下面的範例中,我們將建立 3 個檔案,如下所示

版面配置檔案

與 JSP 自訂標籤一樣,版面配置在專用檔案中描述。語法非常相似,只是我們正在使用 tiles 標籤庫。

<html xmlns:tiles="http://tiles.apache.org/tags-tiles" …>
<head> … </head>
 <body>
   <jsp:include page="/WEB-INF/view/jsp/menu.jspx"/>
   <tiles:insertAttribute name="main" />
   <jsp:include page="/WEB-INF/view/jsp/footer.jspx"/>
 </body>
</html>

layout.jspx

 

上面範例中最重要的指令是 <tiles:insertAttribute />。當範本被處理時,<tiles:insertAttribute /> 會被來自「main」JSP 的內容替換。然後,我們將使用一個專用檔案(通常稱為 tiles.xml),其中包含所有 tiles 定義,如下所示


<tiles-definitions>
 <definition name="tiles/*" template="/WEB-INF/view/jsp/03-tiles/layout.jspx">
  <put-attribute name="main" value="/WEB-INF/view/jsp/03-tiles/{1}.jspx" />
 </definition>
</tiles-definitions>

tiles.xml

[標題=萬用字元用法]過去,Apache Tiles 不處理萬用字元,每次建立新的 JSP 時,我們都必須在 tiles.xml 內部複製/貼上新的定義。[/標題]

基於上面的範例,視圖「tiles/users」將使用範本 layout.jspx 解析為 /WEB-INF/view/jsp/users.jspx

在 JSP 內部,沒有提及它使用的版面配置


<div xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:jsp="http://java.sun.com/JSP/Page">
<table>
 <thead>
   <tr>
    <th> First Name </th>
    <th> Last name </th>
   </tr>
  </thead>
  <tbody>
  <c:forEach var="user" items="${users}">
   <tr>
     <td> <c:out value="${user.firstName}"/> </td>
     <td> <c:out value="${user.lastName}"/> </td>
   </tr>
  </c:forEach>
  </tbody>
 </table>
</div>

Apache Tiles 方法與自訂標籤類似,因此具有相同的優點和缺點。Apache Tiles 專案有一些活動,但絕對不像 ThymeLeaf 那樣充滿活力,我們將在下一節中討論它。

Thymeleaf

Thymeleaf 將自身定義為一個 XML / XHTML / HTML5 範本引擎。

它不是基於 JSP,而是基於一些帶有一點命名空間魔法的純 HTML 檔案。

第一步:我們應該將 ThymeLeaf 與 Spring 整合。像往常一樣,我們需要宣告適當的視圖解析器。


<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
  <property name="prefix" value="/WEB-INF/view/" />
  <property name="suffix" value=".html" />
  <property name="templateMode" value="HTML5" />
  <property name="cacheable" value="false" />
 </bean>
 <bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
  <property name="templateResolver" ref="templateResolver" />
 </bean>
 <bean class="org.thymeleaf.spring3.view.ThymeleafViewResolver">
  <property name="templateEngine" ref="templateEngine" />
  <property name="order" value="1" />
  <property name="viewNames" value="thymeleaf/*" />
 </bean>

現在讓我們考慮一個簡單的視圖頁面。


<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <link th:src="@{/style/app.css}" rel="stylesheet"/>
  </head>
  <body>
    <div th:if="${not #lists.isEmpty(users)}">
    <table>
     <thead> … </thead>
     <tbody>
<tr th:each="user : ${users}">
        <td th:text="${user.firstName}">John</td>
        <td th:text="${user.lastName}">Smith</td>
</tr>
     </tbody>
    </table>
</div></body></html>

users.html

我們可以注意到幾件事

- 這是一個 html 檔案!您實際上可以在 Web 瀏覽器中將其預覽為靜態檔案。此功能非常適合原型設計 [1]。

  • 我們正在使用專用的命名空間,以便將靜態 html 頁面轉換為動態視圖。所有需要動態處理的部分都以「th:」為前綴。

  • 使用 ‘@{…}’ 引用上下文路徑很簡單。 這在純 JSP 中很容易出錯 [2]。

  • ${users} 使用 Spring Expression Language 解析。如果我有表單,我將使用諸如 *{user.name} 之類的表達式來引用表單元素。

[1] 我們將不在本文中進一步討論原型設計。但是,如果您想了解更多相關資訊,可以閱讀本教程 (http://www.thymeleaf.org/petclinic.html)。

[2] 在本文的第一部分中,當使用 <jsp:include /> 時,我必須使用相對路徑  *“../menu.jspx”*。當我將 JSP 檔案移動到不同的資料夾時,這將導致連結中斷。

版面配置檔案

現在讓我們討論如何將版面配置外部化到專用檔案中。

正如我們對 JSP 自訂標籤和 Tiles 所做的那樣,您需要宣告您的版面配置檔案。在下面的程式碼範例中,您將找到 2 個片段

  • headerFragment 包含所有頁首資訊

  • menuFragment 包含我的選單列

這些名稱不是強制性的,我可以擁有任意數量的片段。

在每個視圖檔案中,我可以按如下方式使用 th:include 引用片段


<html xmlns:th="http://www.thymeleaf.org">
 <head th:include="thymeleaf/layout :: headerFragment">
 <!-- replaced with fragment content -->
 <!—- 'thymeleaf/layout' refers to /thymeleaf/layout.html on the filesystem -->
 </head>

 <body>

 <div th:include="thymeleaf/layout :: menuFragment">
 </div>
 <div th:if="${not #lists.isEmpty(users)}">
 <table>
   …
   <tbody>
     <tr th:each="user : ${users}">
      <td th:text="${user.firstName}">John</td>
      <td th:text="${user.lastName}">Smith</td>
     </tr>
   </tbody>
  </table>
 </div>
 </body>
</html>

在檔案系統上,我們有

優點與缺點

從好的方面來說
  • ThymeLeaf 是一個健康的開源專案:每個月都會推出新功能、良好的文件、反應靈敏的使用者論壇…
  • 如果您希望您的網頁設計師能夠閱讀您的視圖檔案,那麼它是理想的範本引擎
  • 使用的表達式語言(實際上稱為標準方言)比 JSP 表達式語言強大得多。
  • 與 JSP 不同,Thymeleaf 非常適合富 HTML 電子郵件(請參閱 http://www.thymeleaf.org/springmail.html)。
缺點是
  • Thymeleaf 尚沒有與自訂標籤 (.tagx 檔案) 等效的功能。
  • 在這個階段,ThymeLeaf 與 JSP 標籤庫不相容。

結論

我們已經並排看到了 JSP 和 Thymeleaf 方法。如果您的應用程式使用數百個 JSP,我們並不是說您應該拋棄所有 JSP 並使用 Thymeleaf 重新開始。但是,您可以考慮將 Thymeleaf 用於 Web 容器外部的 HTML 頁面,例如用於富 HTML 電子郵件。

如果您要開始一個新專案,我們強烈建議您比較 Thymeleaf 和 JSP,以找出哪一個更適合您的需求。

此外,我的同事 Rob Winch 做了一個關於 Spring MVC 的現代範本選項的精彩簡報。除了 JSP 和 Thymeleaf 之外,它還討論了 Mustache 範本。

此部落格文章使用的範例應用程式在 github 上可用

取得 Spring 電子報

保持與 Spring 電子報的聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部