領先一步
VMware 提供培訓和認證,為您的進步加速。
瞭解更多在檢視層方面,Spring @MVC 提供了多種選擇。在本文中,我們將首先討論過去幾年中最可能使用的檢視層方式:JSP。我們將看到使用它們的好壞方式(純 JSP、帶自定義標籤的 JSP、Apache Tiles)。
接下來我們將討論一個名為 Thymeleaf 的新專案,您可以將其用作 JSP 的替代方案。
像往常一樣,您可以在 github 上的相應應用中找到討論的所有程式碼示例。
<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 年前。這是否意味著它已經過時了?讓我們討論一些可能的限制。
我們使用了 <jsp:include /> 來包含 JSP 片段(頁首和頁尾)。顯然,有 <jsp:include /> 是好的,因為它避免了大量複製貼上。然而,如果您有數百個 JSP 檔案,您會發現自己不得不在所有 JSP 中複製貼上那些 <jsp:include /> 標籤。更好的做法是將所有佈局資訊外部化到一個專門的檔案中。
我們的使用者頁面相當小,因為它只是簡單地顯示一個元素列表。我們已經有 50 行程式碼(上面的程式碼示例已略微 精簡)。您可以想象如果我們需要顯示大量內容時它會有多大。
此頁面不符合 HTML/CSS 規範。假設一個網頁設計師已經對其進行了原型設計,為了使用侵入性的 JSP 語法,您將不得不完全重寫它。我們將在談論 ThymeLeaf 時回到這一點。
這裡有一個例子
<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 /> 將被替換為“主”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 的基礎上提供了一組標籤,因此允許您無需自己編寫 Javascript 即可建立 AJAX 風格的資料表。文件寫得很好,並且該專案正在積極開發中。
首先,您應該宣告相應的 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 個檔案
<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 /> 將被替換為“主”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 活躍。
它不基於 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 檔案!您實際上可以在網頁瀏覽器中將其作為靜態檔案預覽。這個功能非常適合原型設計 [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,以便確定哪種更適合您的需求。
此外,我的同事 Rob Winch 做了一個很棒的關於 Spring MVC 現代模板選項 的演示。除了 JSP 和 Thymeleaf,他還討論了 Mustache 模板。
此部落格文章使用的示例應用可在 github 上找到。