解耦邏輯:概念
到目前為止,模闆都是以正常的方式完成,邏輯以屬性的形式插入模闆中。
但是Thymeleaf還允許我們将模闆标記與其邏輯完全分離,進而允許在 HTML 和 XML 模闆模式下建立完全無邏輯的标記模闆。
主要思想是模闆邏輯将在單獨的邏輯檔案中定義(更确切地說是邏輯資源,因為它不必是file)。預設情況下,該邏輯資源将是與模闆檔案位于同一位置(例如,檔案夾)的附加檔案,其名稱相同,但擴充名為 .th.xml:
/templates +->/home.html +->/home.th.xml
是以,home.html 檔案可以完全沒有邏輯。它可能看起來像這樣:
<!DOCTYPE html>
<html>
<body>
<table id="usersTable">
<tr>
<td class="username">Jeremy Grapefruit</td>
<td class="usertype">Normal User</td>
</tr>
<tr>
<td class="username">Alice Watermelon</td>
<td class="usertype">Administrator</td>
</tr>
</table>
</body>
</html>
這裡絕對沒有Thymeleaf代碼,這是一個模闆檔案,不具備Thymeleaf或模闆知識的設計人員可以建立,編輯或了解、或某些外部系統完全沒有Thymeleaf鈎子提供的HTML片段。
現在,home.html 通過建立如下所示的其他 home.th.xml 檔案,将該模闆轉換為Thymeleaf模闆:
<?xml version="1.0"?>
<thlogic>
<attr sel="#usersTable" th:remove="all-but-first">
<attr sel="/tr[0]" th:each="user : ${users}">
<attr sel="td.username" th:text="${user.name}" />
<attr sel="td.usertype" th:text="#{|user.type.${user.type}|}" />
</attr>
</attr>
</thlogic>
在這裡,我們可以看到一個 thlogic 塊内有很多 <attr> 标簽。這些 <attr> 标簽對通過其屬性選擇的原始模闆的節點執行屬性注入,這些 sel 屬性包含Thymeleaf标記選擇器(實際上是AttoParser标記選擇器)。
另請注意,<attr> 可以嵌套标簽,以便附加選擇器。例如,上述中的 sel="/tr[0]",将被處理為sel="#usersTable/tr[0]"。使用者名的選擇器 <td> 将被處理為 sel="#usersTable/tr[0]//td.username"。
是以,一旦合并,上面看到的兩個檔案将與以下内容相同:
<!DOCTYPE html>
<html>
<body>
<table id="usersTable" th:remove="all-but-first">
<tr th:each="user : ${users}">
<td class="username" th:text="${user.name}">Jeremy Grapefruit</td>
<td class="usertype" th:text="#{|user.type.${user.type}|}">Normal User</td>
</tr>
<tr>
<td class="username">Alice Watermelon</td>
<td class="usertype">Administrator</td>
</tr>
</table>
</body>
</html>
與建立兩個單獨的檔案相比,這看起來更熟悉,并且确實不那麼冗長。但是,解耦模闆的優勢在于,我們可以使模闆完全獨立于Thymeleaf,是以從設計的角度來看,它具有更好的可維護性。
當然,設計人員和開發人員之間仍然需要一些約定,例如,使用者 <table> 将需要一個id="usersTable",但是在許多情況下,純HTML模闆将是設計團隊和開發團隊之間更好的溝通工具。
配置解耦模闆
預設情況下,不會期望每個模闆都需求解耦邏輯。相反,已配置的模闆解析器(ITemplateResolver的實作)需要專門标記他們解析的模闆為使用解耦邏輯。
除了StringTemplateResolver(不允許解耦邏輯)外,的所有其他現成 ITemplateResolver 實作都将提供一個useDecoupledLogic 标志,該标志将該解析器解析的所有模闆标記為可能将其全部或部分邏輯存儲在單獨的資源中:
final ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(servletContext);
...
templateResolver.setUseDecoupledLogic(true);
混合耦合和解耦邏輯
啟用後,解耦模闆邏輯不是必需的。啟用後,這意味着引擎将查找包含解耦邏輯的資源,如果存在,則将其解析并與原始模闆合并。如果解耦的邏輯資源不存在,則不會引發任何錯誤。
同樣,在同一模闆中,我們可以混合使用耦合邏輯和解耦邏輯,例如,通過在原始模闆檔案中添加一些Thymeleaf屬性,而将其他屬性留給單獨的解耦邏輯檔案。最常見的情況是使用新的(在v3.0中)th:ref 屬性。
th:ref 屬性
th:ref 隻是标記屬性。從處理的角度來看,它什麼也沒做,隻是在處理模闆後消失,但是它的作用在于它充當标記引用,即它可以像标記名或片段(th:fragment)一樣通過标記選擇器中的名稱來解析。
是以,如果我們有一個選擇器,例如:
<attr sel="whatever" .../>
這将比對:
- 任何 <whatever> 标簽。
- 具有 th:fragment="whatever" 屬性的任何标簽。
- 具有 th:ref="whatever" 屬性的任何标簽。
例如,使用純HTML id屬性,使用 th:ref 的優勢是什麼?事實上,我們可能不想在标簽中添加這麼多 id 和 class 屬性來充當邏輯錨,這可能會污染我們的輸出。
從同樣的意義上說,th:ref 的缺點是什麼?很顯然,我們會在模闆中添加一些Thymeleaf邏輯(“邏輯”)。
請注意,th:ref 屬性的适用性不僅适用于解耦的邏輯模闆檔案:它在其他類型的場景中也一樣工作,例如在片段表達式(~{...})中。
解耦模闆的性能影響
影響極小。當一個已解析的模闆被标記為使用解耦邏輯并且不被緩存時,該模闆邏輯資源将首先被解析,解析并處理為一系列記憶體中的指令:基本上是要注入到每個标記選擇器的屬性清單。
但這是唯一需要執行的附加步驟,因為在此之後,将解析真實模闆,并且在解析這些模闆時,由于AttoParser中節點選擇的進階功能,這些屬性将由解析器本身即時注入。。是以,已解析的節點将從解析器中出來,就像它們的注入屬性寫在原始模闆檔案中一樣。
這樣最大的優勢?将模闆配置為要緩存時,它将緩存已包含注入屬性的模闆。是以,一旦對高速緩存的模闆使用解耦模闆,其開銷将絕對為零。
解耦邏輯的解析
Thymeleaf解析與每個模闆相對應的解耦邏輯資源的方式可由使用者配置。它由擴充點org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver決定,并且為其提供了預設實作:StandardDecoupledTemplateLogicResolver。
此标準實作有什麼作用?
- 首先,它将 prefix 和 suffix 應用于模闆資源的基本名稱(通過其 ITemplateResource#getBaseName() 方法獲得)。字首和字尾都可以配置,預設情況下,字首為空,字尾為 .th.xml。
- 其次,它要求模闆資源通過其ITemplateResource#relative(String relativeLocation)方法來解析具有所計算名稱的相對資源。
IDecoupledTemplateLogicResolver 可以 TemplateEngine 輕松配置要使用的具體實作:
final StandardDecoupledTemplateLogicResolver decoupledresolver =
new StandardDecoupledTemplateLogicResolver();
decoupledResolver.setPrefix("../viewlogic/");
...
templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);

回複以下關鍵字,擷取更多資源
SpringCloud進階之路 | Java 基礎 | 微服務 | JAVA WEB | JAVA 進階 | JAVA 面試 | MK 精講

筆者開通了個人微信公衆号【銀河架構師】,分享工作、生活過程中的心得體會,填坑指南,技術感悟等内容,會比部落格提前更新,歡迎訂閱。