日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

fundamentals\java\Thymeleaf

發(fā)布時間:2024/3/24 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 fundamentals\java\Thymeleaf 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Thymeleaf

翻譯自:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html

up主用google蝦譯的,和up主完全無關,強裂建議直接看原文。

主要就是幾個符號啦:

消息:#{}

表達式有作用域:${}?

表達式無作用域:*{}

url:@{}

相對地址:~{}???

js內轉義特殊字符:[[${})]

js內原生格式:[(${})],

dom屬性:th:each th:object th:href th:url

內置函數(shù):#ctx #request ......

Thymeleaf也沒有什么大不了的。

目錄

Thymeleaf

1簡介

1.1什么是Thymeleaf?

1.2 Thymeleaf可以處理什么樣的模板?

1.3方言:標準方言

2 設計良好的虛擬雜貨店

2.1雜貨店網站

2.2創(chuàng)建和配置模板引擎

模板解析器

模板引擎

3 文本

3.1多語言的歡迎

使用th:文本和外部化文本

上下文

執(zhí)行模板引擎

3.2更多關于文本和變量的信息

非轉義的文本

使用變量

4標準表達式語法

4.1 消息

?4.2 變量

基本對象表達式

擴展表達式對象

4.3表達式的作用域(星號語法)

4.4 URL鏈接

4.5 模板

4.6 常量

4.7 追加文字

4.8 常量替換

4.9 數(shù)學計算

4.10 比較和相等

4.11 條件表達式

4.12 默認值

4.13 無操作

4.14 數(shù)據(jù)轉換和格式化

4.15 預處理

5 設置屬性

5.1 設置任意屬性

5.2特定屬性賦值

5.3 一次性設置多個值

5.4 追加和追加在前面

5.5 布爾值屬性

5.6 設置任意屬性的值

5.7 支持HTML5屬性

6 迭代

6.1 基礎迭代

th:each

可以被迭代的值

6.2 迭代狀態(tài)

6.3 延遲加載

7 條件表達式

7.1 簡單條件:if 和 unless

7.2 Switch表達式

8 布局模板

8.1 引用模板

定義和引用模板

模板規(guī)格

不使用?th:fragment引用模板

th:insert??th:replace?(and?th:include)區(qū)別

8.2 模板參數(shù)

沒有片段參數(shù)的片段局部變量

模板內斷言

8.3 靈活的布局:不僅僅是片段插入

使用空模板

使用_操作符

模板的高級條件引用

8.4 刪除多余的靜態(tài)html代碼

8.5 布局繼承

9 本地參數(shù)

10屬性優(yōu)先級

11 注釋

11.1. 標志HTML/XML注釋

11.2. Thymeleaf 注釋模塊

11.3. Thymeleaf原型注釋

11.4.th:block

12 Thymeleaf嵌入

12.1 表達式嵌入

嵌入vs非嵌入

禁止嵌入

12.2 文本嵌入

12.3 JavaScript 嵌入

JavaScript 非嵌入寫法

嵌入求值和js序列號

12.4 CSS嵌入

高級特性

13 文本模式

13.1 文本語法

忽略元素屬性

13.2擴展

13.3 原型注釋

13.4 文本解析器級注釋塊:刪除代碼

13.5 JavaScript and CSS 模板

14雜貨店(略)

15 配置

15.1 模板解析器

模板解析器鏈

15.2 消息解析器

標準消息解析器

配置消息解析器

15.3 轉換服務

15.4 日志

16 模板緩存

17 離線模板

17.1 離線模板的概念(分離thymeleaf和dom)

17.2 配置離線模板

開啟離線模板

混用在線/離線模板

17.3 ref屬性

17.4 離線模板的性能

17.5 何時分離邏輯代碼和樣式(離線模板)


1簡介

1.1什么是Thymeleaf?

Thymeleaf是一個用于web和獨立環(huán)境的現(xiàn)代服務器端Java模板引擎,能夠處理HTMLXMLJavaScriptCSS甚至純文本。

Thymeleaf的主要目標是提供一種優(yōu)雅且易于維護的創(chuàng)建模板的方法。為了實現(xiàn)這一點,它基于自然模板的概念,以一種不影響模板作為設計原型使用的方式將其邏輯注入模板文件中。這改善了設計的交流,并彌合了設計和開發(fā)團隊之間的差距。

從一開始,Thymeleaf的設計就考慮到了Web標準——尤其是HTML5——如果需要的話,它允許您創(chuàng)建完全驗證模板。

1.2 Thymeleaf可以處理什么樣的模板?

開箱即用,Thymeleaf允許您處理六種模板,每一種都被稱為模板模式:

  • HTML
  • XML
  • TEXT
  • JAVASCRIPT
  • CSS
  • RAW

有兩種標記模板模式(HTMLXML)、三種文本模板模式(文本、JAVASCRIPTCSS)和無操作模板模式(RAW)

HTML模板模式將允許任何類型的HTML輸入,包括HTML5HTML 4XHTML。不執(zhí)行驗證或格式良好性檢查,模板代碼/結構將在輸出中得到最大程度的尊重。

XML模板模式將允許XML輸入。在這種情況下,代碼應該是格式良好的沒有未關閉的標記,沒有未引用的屬性,等等如果發(fā)現(xiàn)違反格式良好的情況,解析器將拋出異常。注意,不會執(zhí)行(針對DTDXML模式)驗證。

文本模板模式將允許對非標記性質的模板使用特殊語法。此類模板的示例可能是文本電子郵件或模板化文檔。注意,HTMLXML模板也可以作為文本處理,在這種情況下,它們不會被解析為標記,并且每個標記、DOCTYPE、注釋等都將被視為純文本。

JAVASCRIPT模板模式將允許在Thymeleaf應用程序中處理JAVASCRIPT文件。這意味著能夠像在HTML文件中那樣在JavaScript文件中使用模型數(shù)據(jù),但是使用特定于JavaScript的集成,如專門轉義或自然腳本。JAVASCRIPT模板模式被認為是文本模式,因此使用與文本模板模式相同的特殊語法。

CSS模板模式將允許處理Thymeleaf應用程序中涉及的CSS文件。與JAVASCRIPT模式類似,CSS模板模式也是文本模式,使用來自文本模板模式的特殊處理語法。

原始模板模式將完全不處理模板。它用于將未接觸的資源(文件、URL響應等)插入正在處理的模板中。例如,可以將HTML格式的外部不受控制的資源包含到應用程序模板中,并且安全地知道這些資源可能包含的任何Thymeleaf代碼都不會被執(zhí)行。

1.3方言:標準方言

2 設計良好的虛擬雜貨店

本文所示示例的源代碼以及本指南的后續(xù)章節(jié)可以在Good中找到

Thymes Virtual Grocery GitHub repository.

2.1雜貨店網站

我們的應用程序還有一個非常簡單的服務層,由包含如下方法的服務對象組成:

public class ProductService {...public List<Product> findAll() {return ProductRepository.getInstance().findAll();}public Product findById(Integer id) {return ProductRepository.getInstance().findById(id);}}

web層,我們的應用程序將有一個過濾器,根據(jù)請求URL將執(zhí)行委托給支持thymeleaf的命令:

private boolean process(HttpServletRequest request, HttpServletResponse response)throws ServletException {try {// This prevents triggering engine executions for resource URLsif (request.getRequestURI().startsWith("/css") ||request.getRequestURI().startsWith("/images") ||request.getRequestURI().startsWith("/favicon")) {return false;}/** Query controller/URL mapping and obtain the controller* that will process the request. If no controller is available,* return false and let other filters/servlets process the request.*/IGTVGController controller = this.application.resolveControllerForRequest(request);if (controller == null) {return false;}/** Obtain the TemplateEngine instance.*/ITemplateEngine templateEngine = this.application.getTemplateEngine();/** Write the response headers*/response.setContentType("text/html;charset=UTF-8");response.setHeader("Pragma", "no-cache");response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expires", 0);/** Execute the controller and process view template,* writing the results to the response writer. */controller.process(request, response, this.servletContext, templateEngine);return true;} catch (Exception e) {try {response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);} catch (final IOException ignored) {// Just ignore this}throw new ServletException(e);}}

這是我們的IGTVGController接口:

我們現(xiàn)在要做的就是創(chuàng)建IGTVGController接口的實現(xiàn),從服務中檢索數(shù)據(jù),并使用ITemplateEngine對象處理模板

最后,它會是這樣的:

但是首先讓我們看看模板引擎是如何初始化的。

2.2創(chuàng)建和配置模板引擎

我們過濾器中的process(…)方法包含這一行:

ITemplateEngine templateEngine = this.application.getTemplateEngine();

這意味著GTVGApplication類負責創(chuàng)建和配置Thymeleaf應用程序中最重要的對象之一:TemplateEngine實例(ITemplateEngine接口的實現(xiàn))

我們的org.thymeleafTemplateEngine對象是這樣初始化的:

public class GTVGApplication {...private final TemplateEngine templateEngine;...public GTVGApplication(final ServletContext servletContext) {super();ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);// HTML is the default mode, but we set it anyway for better understanding of codetemplateResolver.setTemplateMode(TemplateMode.HTML);// This will convert "home" to "/WEB-INF/templates/home.html"templateResolver.setPrefix("/WEB-INF/templates/");templateResolver.setSuffix(".html");// Template cache TTL=1h. If not set, entries would be cached until expelledtemplateResolver.setCacheTTLMs(Long.valueOf(3600000L));// Cache is set to true by default. Set to false if you want templates to// be automatically updated when modified.templateResolver.setCacheable(true);this.templateEngine = new TemplateEngine();this.templateEngine.setTemplateResolver(templateResolver);...}}

有很多方法可以配置TemplateEngine對象,但是現(xiàn)在這幾行代碼可以告訴我們所需的步驟

模板解析器

讓我們從模板解析器開始:

ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);

模板解析器是實現(xiàn)Thymeleaf API接口的對象,該接口名為org.thymeleaf.templateresolver.ITemplateResolver:

public interface ITemplateResolver {.../** Templates are resolved by their name (or content) and also (optionally) their * owner template in case we are trying to resolve a fragment for another template.* Will return null if template cannot be handled by this template resolver.*/public TemplateResolution resolveTemplate(final IEngineConfiguration configuration,final String ownerTemplate, final String template,final Map<String, Object> templateResolutionAttributes); }

這些對象負責決定如何訪問我們的模板,在這個GTVG應用程序中,是org.thymeleaf.templateresolverservletcontext意味著我們將從Servlet上下文中檢索模板文件作為資源:一個應用程序范圍的javax.servletServletContext對象,它存在于每個Java web應用程序中,并解析來自web應用程序根目錄的資源。

但這并不是關于模板解析器的全部內容,因為我們可以在其上設置一些配置參數(shù)。第一,模板模式:

templateResolver.setTemplateMode(TemplateMode.HTML);

HTMLservlet context ttemplateresolver的默認模板模式,但是無論如何建立它都是一個很好的實踐,這樣我們的代碼就可以清楚地記錄發(fā)生了什么。

templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html");

前綴和后綴修改我們將傳遞給引擎的模板名稱,以獲得要使用的實際資源名稱。

使用此配置,模板名稱“product/list”對應于:

servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")

可選地,通過cacheTTLMs屬性在模板解析器上配置已解析模板可以駐留在緩存中的時間:

templateResolver.setCacheTTLMs(3600000L);

如果達到最大緩存大小,并且它是當前緩存的最古老的條目,那么在到達TTL之前,仍然可以從緩存中驅逐模板。

用戶可以通過實現(xiàn)ICacheManager接口或修改StandardCacheManager對象來管理默認緩存來定義緩存行為和大小。

關于模板解析器還有很多要了解的,但是現(xiàn)在讓我們來看看模板引擎對象的創(chuàng)建。

模板引擎

模板引擎對象是org.thymeleaf的實現(xiàn)。ITemplateEngine接口。這些實現(xiàn)之一是由Thymeleaf核心提供的:org.thymeleafTemplateEngine,我們在這里創(chuàng)建一個實例:

templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver);

很簡單,不是嗎?我們只需要創(chuàng)建一個實例并將模板解析器設置為它。

模板解析器是TemplateEngine需要的惟一必需參數(shù),不過后面還將介紹其他許多參數(shù)(消息解析器、緩存大小等)。現(xiàn)在,這就是我們所需要的。

我們的模板引擎現(xiàn)在已經準備好了,我們可以開始使用Thymeleaf創(chuàng)建頁面。

3 文本

3.1多語言的歡迎

使用th:文本和外部化文本

上下文

執(zhí)行模板引擎

3.2更多關于文本和變量的信息

非轉義的文本

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

使用變量

<body><p th:utext="#{home.welcome}">Welcome to our grocery store!</p><p>Today is: <span th:text="${today}">13 February 2011</span></p></body>

4標準表達式語法

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p><p>Today is: <span th:text="${today}">13 february 2011</span></p>

Simple expressions:

  • Variable Expressions:?${...}
  • Selection Variable Expressions:?*{...}
  • Message Expressions:?#{...}
  • Link URL Expressions:?@{...}
  • Fragment Expressions:?~{...}

Literals 常量

  • Text literals:?'one text',?'Another one!',…
  • Number literals:?0,?34,?3.0,?12.3,…
  • Boolean literals:?true,?false
  • Null literal:?null
  • Literal tokens:?one,?sometext,?main,…

Text operations:

  • String concatenation:?+
  • Literal substitutions:?|The name is ${name}|

Arithmetic operations:

  • Binary operators:?+,?-,?*,?/,?%
  • Minus sign (unary operator):?-

Boolean operations:

  • Binary operators:?and,?or
  • Boolean negation (unary operator):?!,?not

Comparisons and equality:

  • Comparators:?>,?<,?>=,?<=?(gt,?lt,?ge,?le)
  • Equality operators:?==,?!=?(eq,?ne)

Conditional operators:

  • If-then:?(if) ? (then)
  • If-then-else:?(if) ? (then) : (else)
  • Default:?(value) ?: (defaultvalue)

Special tokens:

  • No-Operation:?_

所有這些功能可以組合和嵌套:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

4.1 消息

如我們所知,……消息表達式允許我們鏈接這個:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

消息鍵本身可以來自一個變量:

<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">Welcome to our grocery store, Sebastian Pepper! </p>

?4.2 變量

<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>

實際上等于

ctx.getVariable("today");

但是OGNL允許我們創(chuàng)建更強大的表達式,這就是它的原理:

<p th:utext="#{home.welcome(${session.user.name})}">Welcome to our grocery store, Sebastian Pepper! </p>

通過執(zhí)行以下命令獲得用戶名:

((User) ctx.getVariable("session").get("user")).getName();

但是getter方法導航只是OGNL的特性之一。讓我們再看一些:

/** Access to properties using the point (.). Equivalent to calling property getters.*/ ${person.father.name}/** Access to properties can also be made by using brackets ([]) and writing * the name of the property as a variable or between single quotes.*/ ${person['father']['name']}/** If the object is a map, both dot and bracket syntax will be equivalent to * executing a call on its get(...) method.*/ ${countriesByCode.ES} ${personsByName['Stephen Zucchini'].age}/** Indexed access to arrays or collections is also performed with brackets, * writing the index without quotes.*/ ${personsArray[0].name}/** Methods can be called, even with arguments.*/ ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')}

基本對象表達式

當在上下文變量上計算OGNL表達式時,為了獲得更高的靈活性,表達式可以使用一些對象。這些對象將被引用(按照OGNL標準),從#符號開始:

  • #ctx: the context object.
  • #vars:?the context variables.
  • #locale: the context locale.
  • #request: (only in Web Contexts) the?HttpServletRequest?object.
  • #response: (only in Web Contexts) the?HttpServletResponse?object.
  • #session: (only in Web Contexts) the?HttpSession?object.
  • #servletContext: (only in Web Contexts) the?ServletContext?object.

我們可以這樣做:

Established locale country: <span th:text="${#locale.country}">US</span>. ?

擴展表達式對象

除了這些基本對象之外,Thymeleaf還將提供一組實用程序對象,幫助我們在表達式中執(zhí)行常見的任務。

  • #execInfo: information about the template being processed.
  • #messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
  • #uris: methods for escaping parts of URLs/URIs
  • #conversions: methods for executing the configured?conversion service?(if any).
  • #dates: methods for?java.util.Date?objects: formatting, component extraction, etc.
  • #calendars: analogous to?#dates, but for?java.util.Calendar?objects.
  • #numbers: methods for formatting numeric objects.
  • #strings: methods for?String?objects: contains, startsWith, prepending/appending, etc.
  • #objects: methods for objects in general.
  • #bools: methods for boolean evaluation.
  • #arrays: methods for arrays.
  • #lists: methods for lists.
  • #sets: methods for sets.
  • #maps: methods for maps.
  • #aggregates: methods for creating aggregates on arrays or collections.
  • #ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).

4.3表達式的作用域(星號語法)

變量表達式不僅可以寫成${…},也可以寫成*{…}

但是有一個重要的區(qū)別:星號語法對所選對象的表達式求值,而不是對整個上下文求值。也就是說,只要沒有選擇對象,美元和星號語法的作用是完全相同的。

當對象選擇就緒時,所選對象也將作為#對象表達式變量提供給美元表達式:

<div th:object="${session.user}"><p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p><p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p><p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div>

如前所述,如果沒有th:object,則美元語法和星號語法是等效的。

<div><p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p><p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p><p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p> </div>

4.4 URL鏈接

由于它們的重要性,urlweb應用程序模板中是一等公民,Thymeleaf標準方言為它們提供了一種特殊的語法,即@語法:@{…}

有不同類型的URL:

絕對url:http://www.thymeleaf.org

相對url,可以是:

  • Page-relative:?user/login.html
  • Context-relative:?/itemdetails?id=3?(context name in server will be added automatically)
  • Server-relative:?~/billing/processInvoice?(allows calling URLs in another context (= application) in the same server.
  • Protocol-relative URLs:?//code.jquery.com/jquery-2.0.3.min.js

這些表達式的實際處理以及它們對將要輸出的url的轉換是由org.thymeleaf.linkbuilder的實現(xiàn)完成的。注冊到正在使用的ITemplateEngine對象中的ILinkBuilder接口。

默認情況下,該接口的單個實現(xiàn)注冊為org.thymeleaf.linkbuilder類。StandardLinkBuilder,這對于離線(web)和基于Servlet APIweb場景都足夠了。其他場景(如與非servletapi web框架的集成)可能需要link builder接口的特定實現(xiàn)。

讓我們使用這個新語法。滿足th:href屬性:

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) --> <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a><!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) --> <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a><!-- Will produce '/gtvg/order/3/details' (plus rewriting) --> <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

這里需要注意的是:

th:href是一個修飾符屬性:一旦處理,它將計算要使用的鏈接URL,并將該值設置為標記的href屬性。

我們可以為URL參數(shù)使用表達式(正如您可以在orderId=${o.id}中看到的那樣)。還將自動執(zhí)行所需的url參數(shù)編碼操作。

如果需要幾個參數(shù),則用逗號分隔:@{/order/process(execId=${execId}execType='FAST')}

URL路徑中也允許使用變量模板:@{/order/{orderId}/details(orderId=${orderId})}

/(例如:/order/details)開頭的相對url將自動以應用程序上下文名稱作為前綴。

如果沒有啟用cookie,或者還不知道,可以向相對url添加一個“;jsessionid=…”后綴,以便保留會話。這稱為URL重寫,Thymeleaf允許您通過使用Servlet API中的response.encodeURL(…)機制為每個URL插入自己的重寫過濾器。

th:href屬性允許我們(可選地)在模板中有一個可工作的靜態(tài)href屬性,這樣在直接為原型目的打開模板鏈接時,瀏覽器就可以繼續(xù)導航。

與消息語法(#{…})一樣,URL基也可以是計算另一個表達式的結果:

<a th:href="@{${url}(orderId=${o.id})}">view</a> <a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>

4.5 模板

最常見的用法是使用th:insertth:replace插入片段(在后面的小節(jié)中將詳細介紹):

<div th:insert="~{commons :: main}">...</div>

在本教程的后面,有一個完整的部分專門討論模板布局,包括對片段表達式的更深入的解釋。

4.6 常量

略。

4.7 追加文字

文本,無論它們是文本還是變量或消息表達式的計算結果,都可以使用+運算符輕松地附加:

<span th:text="'The name of the user is ' + ${user.name}">

4.8 常量替換

文字替換允許對包含變量值的字符串進行簡單的格式化,而不需要在字符串后面加上'…”+“…”

這些替換必須被豎條(|)包圍,例如:

<span th:text="|Welcome to our application, ${user.name}!|">

等于:

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

文字替代可以與其他類型的表達式結合:

<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">

?

只有變量/消息表達式(${…},{…} #{…})允許在|…|文字替換。沒有其他的文字('…'),布爾/數(shù)字符號,條件表達式等。

4.9 數(shù)學計算

還有一些算術運算:+-*/%

<div th:with="isEven=(${prodStat.count} % 2 == 0)">

注意,這些運算符也可以應用于OGNL變量表達式本身(在這種情況下,將由OGNL而不是Thymeleaf標準表達式引擎執(zhí)行):

4.10 比較和相等

表達式中的值可以與><>=<=符號進行比較,并且可以使用==!=操作符檢查是否相等(或是否不相等)。注意,XML規(guī)定不應該在屬性值中使用<>符號,因此應該用&lt;?&gt;

<div th:if="${prodStat.count} &gt; 1">

?

<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')"> ?

一個更簡單的選擇可能是使用這些操作符中存在的文本別名:gt(>)lt(<)ge(>=)le(<=),而不是(!)eq (==) neq/ne (!=)

4.11 條件表達式

條件表達式的作用是僅對兩個表達式中的一個求值,這取決于求值條件的結果(條件本身也是另一個表達式)

讓我們看一個示例片段(引入另一個屬性修飾符th:class):

<tr th:class="${row.even}? 'even' : 'odd'">... </tr>

條件表達式的所有三個部分(condition, thenelse)本身都是表達式,這意味著它們可以是變量(${…)}*{…}),消息(# {…}),url(@{…})或文本(“…”)

條件表達式也可以嵌套使用括號:

<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">... </tr>

Else表達式也可以省略,如果條件為false,則返回null:

<tr th:class="${row.even}? 'alt'">... </tr>

4.12 默認值

<div th:object="${session.user}">...<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p> </div>

4.13 無操作

無操作令牌由下劃線符號(_)表示。

這個令牌背后的思想是指定表達式的期望結果是什么都不做,也就是說,就像根本不存在可處理屬性(例如th:text)一樣。

在其他可能性中,這允許開發(fā)人員使用原型文本作為默認值。例如,代替:

<span th:text="${user.name} ?: 'no user authenticated'">...</span>

……我們可以直接使用“no user authenticated”作為原型文本,這使得代碼從設計角度來說更加簡潔和通用:

<span th:text="${user.name} ?: _">no user authenticated</span>

4.14 數(shù)據(jù)轉換和格式化

Thymeleaf為變量(${…})和選擇(*{…})表達式定義了一個雙括號語法,它允許我們通過配置的轉換服務應用數(shù)據(jù)轉換。

基本上是這樣的:

<td th:text="${{user.lastAccessDate}}">...</td>

注意到雙括號了嗎?:$ {{…}}。它指示Thymeleaf傳遞用戶的結果。轉換服務的lastAccessDate表達式,并要求它在寫入結果之前執(zhí)行格式化操作(轉換為字符串)

假設用戶。lastAccessDate類型為java.util.Calendar,如果一個轉換服務(IStandardConversionService的實現(xiàn))已經注冊,并且包含一個有效的日歷->字符串轉換,那么它將被應用。

IStandardConversionService (StandardConversionService)的默認實現(xiàn)只是在任何轉換為字符串的對象上執(zhí)行. tostring()。有關如何注冊自定義轉換服務實現(xiàn)的更多信息,請參閱配置部分的更多信息。

官方的Thymeleaf -spring3Thymeleaf -spring4集成包透明地將Thymeleaf的轉換服務機制與Spring自己的轉換服務基礎設施集成在一起,這樣在Spring配置中聲明的轉換服務和格式化程序將自動提供給${{…}}{{…}}表達式。

4.15 預處理

除了這些用于表達處理的特性外,Thymeleaf還具有預處理表達式的特性。

<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

注意,法語語言環(huán)境的預處理步驟將創(chuàng)建以下等價的代碼:

<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

預處理字符串可以使用\_\_在屬性中轉義。

5 設置屬性

本章將解釋如何在標記中設置(或修改)屬性值

5.1 設置任意屬性

然后輸入th:attr屬性,以及它改變所設置的標簽屬性值的能力:

<form action="subscribe.html" th:attr="action=@{/subscribe}"><fieldset><input type="text" name="email" /><input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/></fieldset> </form>

但是如果我們想一次設置多個屬性呢?XML規(guī)則不允許在標記中兩次設置屬性,因此th:attr將采用逗號分隔的賦值列表,如:

<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

給定所需的消息文件,這將輸出:

<img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />

5.2特定屬性賦值

現(xiàn)在,你可能會想:

<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>

是一段相當難看的標記。在屬性值中指定賦值可能非常實用,但如果必須一直這樣做,那么它不是創(chuàng)建模板的最優(yōu)雅的方法。

Thymeleaf同意您的觀點,這就是為什么th:attr很少用于模板中。通常,您將使用其他th:*屬性,其任務是設置特定的標記屬性(而不是像th:attr這樣的任何屬性)

例如,要設置value屬性,可以使用th:value:

<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>

這看起來好多了!讓我們嘗試對表單標記中的action屬性執(zhí)行相同的操作:

<form action="subscribe.html" th:action="@{/subscribe}">

5.3 一次性設置多個值

有兩個非常特殊的屬性叫做th:alt-titleth:lang-xmllang,可以用于將兩個屬性同時設置為相同的值。具體地說:

  • th:alt-title?will set?alt?and?title.
  • th:lang-xmllang?will set?lang?and?xml:lang.

5.4 追加和追加在前面

Thymeleaf還提供th:attrappendth:attrprepend屬性,它們將對現(xiàn)有屬性值求值的結果追加(后綴)prepend(前綴)

例如,您可能想要將要添加(不是設置,只是添加)CSS類的名稱存儲在上下文變量中的一個按鈕中,因為要使用的特定CSS類將取決于用戶以前所做的事情:

<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />

5.5 布爾值屬性

HTML有布爾屬性的概念,沒有值的屬性,一個值的前綴表示值為“true”。在XHTML中,這些屬性只取1個值,也就是它本身。

<input type="checkbox" name="option2" checked /> <!-- HTML -->

標準方言包括允許您通過計算條件來設置這些屬性的屬性,因此,如果計算為true,屬性將被設置為其固定值,如果計算為false,屬性將不會被設置:

<input type="checkbox" name="active" th:checked="${user.active}" />

5.6 設置任意屬性的值

Thymeleaf提供了一個默認的屬性處理器,允許我們設置任何屬性的值,即使在標準方言中沒有為它定義特定的th:*處理器。

<span th:whatever="${user.name}">...</span>

將會導致

<span whatever="John Apricot">...</span>

5.7 支持HTML5屬性

還可以使用一種完全不同的語法,以一種更加html友好的方式將處理器應用到模板中。

<table><tr data-th-each="user : ${users}"><td data-th-text="${user.login}">...</td><td data-th-text="${user.name}">...</td></tr> </table>

data-{prefix}-{name}語法是HTML5中編寫定制屬性的標準方法,不要求開發(fā)人員使用任何名稱空間名稱,如th:*Thymeleaf使這種語法自動適用于所有方言(不僅僅是標準方言)

還有一種語法可以指定定制標記:{prefix}-{name},它遵循W3C定制元素規(guī)范(W3C Web組件規(guī)范的一部分)。例如,這可以用于th:block元素(或者也可以用于th),后面的部分將對此進行解釋。

重要提示:這個語法是對名稱空間th:* one的補充,它不會替換它。將來完全不打算廢棄帶有名稱空間的語法。

6 迭代

到目前為止,我們已經創(chuàng)建了一個主頁、一個用戶個人資料頁面以及一個允許用戶訂閱我們的時事通訊的頁面……但是我們的產品呢?為此,我們將需要一種迭代集合中的項的方法來構建我們的產品頁面。

6.1 基礎迭代

th:each

<tr th:each="prod : ${prods}"><td th:text="${prod.name}">Onions</td><td th:text="${prod.price}">2.41</td><td th:text="${prod.inStock}? #{true} : #{false}">yes</td></tr>

上面看到的prod: ${prods}屬性值的意思是對于計算${prods}的結果中的每個元素,使用名為prod的變量中的當前元素重復這個模板片段。讓我們給我們看到的每樣東西命名:

  • We will call?${prods}?the?iterated expression?or?iterated variable.
  • We will call?prod?the?iteration variable?or simply?iter variable.

可以被迭代的值

java.utilList類不是Thymeleaf中惟一可以用于迭代的值。有一個相當完整的對象集,被th:each屬性認為是可迭代的:

  • Any object implementing?java.util.Iterable
  • Any object implementing?java.util.Enumeration.
  • Any object implementing?java.util.Iterator, whose values will be used as they are returned by the iterator, without the need to cache all values in memory.
  • Any object implementing?java.util.Map. When iterating maps, iter variables will be of class?java.util.Map.Entry.
  • Any array.
  • Any other object will be treated as if it were a single-valued list containing the object itself.

6.2 迭代狀態(tài)

在使用th:each時,Thymeleaf提供了一種用于跟蹤迭代狀態(tài)的機制:狀態(tài)變量。

狀態(tài)變量定義在th:每個屬性中,包含以下數(shù)據(jù):

  • The current?iteration index, starting with 0. This is the?index?property.
  • The current?iteration index, starting with 1. This is the?count?property.
  • The total amount of elements in the iterated variable. This is the?size?property.
  • The?iter variable?for each iteration. This is the?current?property.
  • Whether the current iteration is even or odd. These are the?even/odd?boolean properties.
  • Whether the current iteration is the first one. This is the?first?boolean property.
  • Whether the current iteration is the last one. This is the?last?boolean property.

讓我們看看如何在前面的例子中使用它:

<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">

狀態(tài)變量(本例中為iterStat)th:each屬性中定義,方法是在iter變量本身后面寫入其名稱,中間用逗號分隔。就像iter變量一樣,status變量的作用域也限定在包含th:each屬性的標記所定義的代碼片段中。

如果您沒有顯式地設置狀態(tài)變量,Thymeleaf將始終為您創(chuàng)建一個狀態(tài)變量,方法是將Stat添加到迭代變量的名稱后面:

6.3 延遲加載

有時我們可能希望優(yōu)化數(shù)據(jù)集合的檢索(例如從數(shù)據(jù)庫中檢索),以便只有在真正要使用這些集合時才檢索這些集合。

?

實際上,這可以應用于任何數(shù)據(jù)塊,但是考慮到內存集合可能具有的大小,檢索要迭代的集合是本場景中最常見的情況。

?

為了支持這一點,Thymeleaf提供了一種延遲加載上下文變量的機制。實現(xiàn)ILazyContextVariable接口的上下文變量——很可能是通過擴展其LazyContextVariable默認實現(xiàn)實現(xiàn)的——將在執(zhí)行時解析。例如:

context.setVariable("users",new LazyContextVariable<List<User>>() {@Overrideprotected List<User> loadValue() {return databaseRepository.findAllUsers();}});

這個變量可以在不知道它是否懶惰的情況下使用,代碼如下:

<li th:each="u : ${users}" th:text="${u.name}">user name</li>

但同時,如果條件在代碼中計算為false,則永遠不會被初始化(它的loadValue()方法將永遠不會被調用),例如:

<li th:each="u : ${users}" th:text="${u.name}">user name</li>

7 條件表達式

7.1 簡單條件:if 和 unless

有時候,您需要模板的一個片段只出現(xiàn)在滿足特定條件的結果中。

例如,假設我們希望在product表中顯示一列,其中包含每個產品的評論數(shù)量,如果有任何評論,則顯示到該產品的評論詳細信息頁面的鏈接。

為了做到這一點,我們將使用th:if屬性:

th:if="${not #lists.isEmpty(prod.comments)}">view</a>

這里有很多東西值得一看,所以讓我們來看看這條重要的線:

這將創(chuàng)建一個到comments頁面的鏈接(帶有URL /product/comments),并將prodId參數(shù)設置為產品的id,但只有在產品有任何注釋時才會這樣做。

讓我們來看看結果標記:

<a href="/gtvg/product/comments?prodId=2">view</a>

請注意,th:if屬性不僅計算布爾條件。它的功能稍微超出了這一范圍,它將按照以下規(guī)則計算指定的表達式為true:

  • If value is a boolean and is?true.
  • If value is a number and is non-zero
  • If value is a character and is non-zero
  • If value is a String and is not “false”, “off” or “no”
  • If value is not a boolean, a number, a character or a String.

(If value is null, th:if will evaluate to false).

同樣,th:if有一個逆屬性,th:unless,我們可以在前面的例子中使用,而不是在OGNL表達式中使用not:

th:unless="${#lists.isEmpty(prod.comments)}">view</a>

7.2 Switch表達式

還有一種方法可以有條件地顯示內容,它使用Java中等價的交換結構:th:switch / th:case屬性集。

<div th:switch="${user.role}"><p th:case="'admin'">User is an administrator</p><p th:case="#{roles.manager}">User is a manager</p> </div>

請注意,一旦第th:case屬性的值為true,同一切換上下文中的每一個其他th:case屬性的值都為false

默認選項指定為th:case="*":

<div th:switch="${user.role}"><p th:case="'admin'">User is an administrator</p><p th:case="#{roles.manager}">User is a manager</p><p th:case="*">User is some other thing</p> </div>

8 布局模板

8.1 引用模板

定義和引用模板

在我們的模板中,我們通常希望包含來自其他模板的部分,比如頁腳、頁眉、菜單……

為了做到這一點,Thymeleaf需要我們?yōu)榘x這些部分,“fragments”,這可以使用th:fragment屬性來完成。

假設我們想為所有的雜貨頁面添加一個標準的版權頁腳,因此我們創(chuàng)建了一個/WEB-INF/templates/footer.html文件,其中包含以下代碼:

<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><body><div th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery</div></body></html>

上面的代碼定義了一個名為copy的片段,我們可以使用th:insertth:replace屬性(以及th:include,不過自Thymeleaf 3.0以來不再推薦使用它)輕松地將其包含在主頁中:

<body>...<div th:insert="~{footer :: copy}"></div></body>

注意,th:insert需要一個片段表達式(~{…}),這是一個產生片段的表達式。在上面的例子中,這是一個非復雜的片段表達式,(~{})的封裝是完全可選的,所以上面的代碼相當于:

<body>...<div th:insert="footer :: copy"></div></body>

模板規(guī)格

片段表達式的語法非常簡單。有三種不同的格式:

"~{templatename::selector}"

包含在名為templatename的模板上應用指定標記選擇器生成的片段。注意,選擇器可以僅僅是一個片段名稱,所以您可以像上面的~{footer:: copy}中那樣指定像~{templatename::fragmentname}這樣簡單的東西。

?

標記選擇器語法由底層的AttoParser解析庫定義,類似于XPath表達式或CSS選擇器。有關更多信息,請參見附錄C

?

"~{templatename}"

包含名為templatename的完整模板。

注意,在th:insert/th:replace標記中使用的模板名稱必須由模板引擎當前使用的模板解析器解析。

~{::selector}"?or?"~{this::selector}"

插入來自相同模板的片段,匹配選擇器。如果在表達式出現(xiàn)的模板上沒有找到,則會將模板調用(插入)的堆棧遍歷到原始處理的模板(),直到選擇器在某個級別上匹配為止。?

上面例子中的templatenameselector都可以是全功能的表達式(甚至是條件語句!)

<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

片段可以包括任何th:*屬性。一旦片段被包含到目標模板中(具有th:insert/th:replace屬性的那個),這些屬性將被計算,并且它們將能夠引用在這個目標模板中定義的任何上下文變量。

?

這種片段處理方法的一大優(yōu)點是,您可以將片段編寫在瀏覽器完全可顯示的頁面中,頁面具有完整甚至有效的標記結構,同時仍然能夠使Thymeleaf將它們包含到其他模板中。

不使用?th:fragment引用模板

由于標記選擇器的強大功能,我們可以包含不使用任何th:fragment屬性的片段。它甚至可以是來自完全不了解Thymeleaf的另一個應用程序的標記代碼:

<div id="copy-section">&copy; 2011 The Good Thymes Virtual Grocery </div>

我們可以使用上面的片段簡單地引用它的id屬性,以類似于CSS選擇器的方式:

<body>...<div th:insert="~{footer :: #copy-section}"></div></body>

th:insert??th:replace?(and?th:include)區(qū)別

th:insertth:replace(以及th:include, 3.0以后就不推薦了)之間的區(qū)別是什么?

th:insert?是最簡單的:它將簡單地插入指定的片段作為其宿主標記的主體。

Th:replace 實際上用指定的片段替換其主機標記。

th:include 類似于th:insert,但它只插入片段的內容,而不是插入片段。

像這樣的HTML片段:

<footer th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery </footer>

Insert

<div><footer>&copy; 2011 The Good Thymes Virtual Grocery</footer></div>

Replace

<footer>&copy; 2011 The Good Thymes Virtual Grocery</footer>

Include

<div>&copy; 2011 The Good Thymes Virtual Grocery </div>

8.2 模板參數(shù)

為了為模板片段創(chuàng)建更類似函數(shù)的機制,使用th:fragment定義的片段可以指定一組參數(shù):

<div th:fragment="frag (onevar,twovar)"><p th:text="${onevar} + ' - ' + ${twovar}">...</p> </div>

這需要使用這兩種語法中的一種來調用來自th:insertth:replace的片段:

<div th:replace="::frag (${value1},${value2})">...</div> <div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

注意最后一個選項中的順序并不重要:

沒有片段參數(shù)的片段局部變量

即使沒有這樣的參數(shù)來定義片段:

<div th:fragment="frag">... </div>

我們可以使用上面指定的第二種語法來調用它們(只有第二種):

<div th:replace="::frag (onevar=${value1},twovar=${value2})">

這將等價于th:replaceth:with:的組合:

<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">

請注意,片段的這種局部變量規(guī)范——無論它是否具有參數(shù)簽名——不會導致在執(zhí)行之前清空上下文。片段仍然能夠像當前一樣訪問調用模板中使用的每個上下文變量。

模板內斷言

斷言屬性可以指定一個以逗號分隔的表達式列表,這些表達式應該被求值,并為每個求值生成true,如果不是,則引發(fā)異常。

<div th:assert="${onevar},(${twovar} != 43)">...</div>

這對于驗證片段簽名的參數(shù)很方便:

<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

8.3 靈活的布局:不僅僅是片段插

多虧了片段表達式,我們可以為片段指定參數(shù),這些片段不是文本、數(shù)字、bean對象……而是標記的片段。

這允許我們以這樣一種方式創(chuàng)建片段,通過調用模板中的標記來豐富片段,從而形成非常靈活的模板布局機制。

注意下面片段中標題和鏈接變量的使用:

<head th:fragment="common_header(title,links)"><title th:replace="${title}">The awesome application</title><!-- Common styles and scripts --><link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}"><link rel="shortcut icon" th:href="@{/images/favicon.ico}"><script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script><!--/* Per-page placeholder for additional links */--><th:block th:replace="${links}" /></head>

We can now call this fragment like:

... <head th:replace="base :: common_header(~{::title},~{::link})"><title>Awesome - Main</title><link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"><link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"></head> ...

結果將使用我們調用模板中實際的

... <head><title>Awesome - Main</title><!-- Common styles and scripts --><link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"><link rel="shortcut icon" href="/awe/images/favicon.ico"><script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script><link rel="stylesheet" href="/awe/css/bootstrap.min.css"><link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"></head> ...

使用空模板

一個特殊的片段表達式,空片段(~{}),可以用來指定沒有標記。使用前面的例子:

<head th:replace="base :: common_header(~{::title},~{})"><title>Awesome - Main</title></head>

注意片段(鏈接)的第二個參數(shù)是如何設置為空片段的,因此沒有為塊編寫任何內容:

... <head><title>Awesome - Main</title><!-- Common styles and scripts --><link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"><link rel="shortcut icon" href="/awe/images/favicon.ico"><script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script></head> ...

使用_操作符

如果我們只想讓片段使用其當前標記作為默認值,那么no-op也可以用作片段的參數(shù)。再次使用common_header示例:

... <head th:replace="base :: common_header(_,~{::link})"><title>Awesome - Main</title><link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"><link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"></head> ...

查看title參數(shù)(common_header片段的第一個參數(shù))是如何設置為no-op(_)的,這將導致片段的這一部分根本不執(zhí)行(title = no-operation):

<title th:replace="${title}">The awesome application</title>

所以結果是:

... <head><title>The awesome application</title><!-- Common styles and scripts --><link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"><link rel="shortcut icon" href="/awe/images/favicon.ico"><script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script><link rel="stylesheet" href="/awe/css/bootstrap.min.css"><link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"></head> ...

模板的高級條件引用

emtpy片段和無操作標記的可用性允許我們以一種非常簡單和優(yōu)雅的方式執(zhí)行片段的條件插入。

例如,我們可以只在用戶是管理員的情況下插入我們的common:: adminhead片段,如果用戶不是管理員,則不插入任何內容(emtpy片段):

? <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>

無操作標記,以便僅在滿足指定條件時插入片段,但如果不滿足條件,則保留標記不做任何修改:

... <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support. </div> ...

此外,如果我們配置了模板解析器來檢查模板資源是否存在——通過它們的checkExistence標志——我們可以使用片段本身的存在作為默認操作的條件:

... <!-- The body of the <div> will be used if the "common :: salutation" fragment --> <!-- does not exist (or is empty). --> <div th:insert="~{common :: salutation} ?: _">Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support. </div> ...

8.4 刪除多余的靜態(tài)html代碼

回到示例應用程序,讓我們重溫產品列表模板的最后一個版本:

<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">

這段代碼作為模板很好,但是作為靜態(tài)頁面(當瀏覽器直接打開而不需要Thymeleaf處理它時)就不是一個很好的原型。

為什么?因為,盡管瀏覽器可以完美地顯示該表,但該表只有一行,而這一行有模擬數(shù)據(jù)。作為一個原型,它看起來不夠真實……我們應該有多個產品,我們需要更多行。

所以讓我們添加一些:

我們需要一種方法在模板處理期間刪除這兩行。

讓我們使用th:remove第二個和第三個<tr>標記上的屬性:

<tr class="odd" th:remove="all">

屬性中的所有值是什么意思呢?th:remove可以有五種不同的行為,這取決于它的值:

  • all: Remove both the containing tag and all its children.
  • body: Do not remove the containing tag, but remove all its children.
  • tag: Remove the containing tag, but do not remove its children.
  • all-but-first: Remove all children of the containing tag except the first one.
  • none?: Do nothing. This value is useful for dynamic evaluation.

這個幾乎是最重要的價值有什么用呢?它將讓我們保存一些th:remove="all"當原型:

th:remove屬性可以接受任何Thymeleaf標準表達式,只要它返回一個允許的字符串值(alltagbodyall-but-firstnone)

這意味著移除可以是有條件的,比如:

<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>

還要注意,th:remove認為nullnone的同義詞,所以下面的工作與上面的示例相同:

<a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>

在這種情況下,如果${condition}false,將返回null,因此不會執(zhí)行刪除操作。

8.5 布局繼承

為了能夠使用單個文件作為布局,可以使用片段。一個簡單的布局的例子,標題和內容使用th:fragmentth:replace:

<!DOCTYPE html> <html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org"> <head><title th:replace="${title}">Layout Title</title> </head> <body><h1>Layout H1</h1><div th:replace="${content}"><p>Layout content</p></div><footer>Layout footer</footer> </body> </html>

這個示例聲明了一個名為layout的片段,其中標題和內容作為參數(shù)。下面的示例中提供的片段表達式將在繼承它的頁面上替換這兩個表達式。

<!DOCTYPE html> <html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}"> <head><title>Page Title</title> </head> <body> <section><p>Page content</p><div>Included on page</div> </section> </body> </html>

在這個文件中,html標記將被layout替換,但是在布局中標題和內容將分別被titlesection塊替換。

如果需要,布局可以由幾個片段組成,如頁眉和頁腳。

9 本地參數(shù)

Thymeleaf調用局部變量,這些變量是為模板的特定片段定義的,并且僅可用于該片段內的求值。

我們已經看到的一個例子是產品列表頁面中的prod iter變量:

<tr th:each="prod : ${prods}">... </tr>

prod變量僅在<tr>標記的范圍內可用。明確地:

對于在該標記中執(zhí)行的優(yōu)先級低于th:each的任何其他th:*屬性,它都是可用的(這意味著它們將在th:each之后執(zhí)行)

prod變量僅在<tr>標記的范圍內可用。明確地:

Thymeleaf提供了一種無需迭代就可以聲明局部變量的方法,使用th:with屬性,其語法類似于屬性值賦值:

< <div th:with="firstPer=${persons[0]}"><p>The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.</p> </div>

th:with被處理時,firstPer變量被創(chuàng)建為一個局部變量,并添加到來自上下文的變量映射中,這樣它就可以與上下文中聲明的任何其他變量一起進行計算,但只能在包含標記的范圍內。

您可以使用通常的多重賦值語法同時定義多個變量:

<div th:with="firstPer=${persons[0]},secondPer=${persons[1]}"><p> The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.</p><p>But the name of the second person is <span th:text="${secondPer.name}">Marcus Antonius</span>.</p> </div>

th:with屬性允許重用在同一個屬性中定義的變量:

<div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>

讓我們用這個在我們的雜貨店的主頁上!還記得我們編寫的輸出格式化日期的代碼嗎?

<p>Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 february 2011</span> </p>

現(xiàn)在,讓我們使用th:with將本地化的日期格式轉換為變量,然后在th:text表達式中使用它:

<p th:with="df=#{date.format}">Today is: <span th:text="${#calendars.format(today,df)}">13 February 2011</span> </p>

這既干凈又簡單。事實上,考慮到th:with的優(yōu)先級高于th:text,我們可以在span標記中解決所有問題:

<p>Today is: <span th:with="df=#{date.format}" th:text="${#calendars.format(today,df)}">13 February 2011</span> </p>

你可能會想:優(yōu)先?我們還沒說呢!別擔心,這正是下一章的內容。

10屬性優(yōu)先級

當您在同一個標記中編寫多個th:*屬性時會發(fā)生什么?例如:

<li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>

我們希望th:每個屬性執(zhí)行之前th:文本,這樣我們得到我們想要的結果,但考慮到HTML / XML標準不給任何意義的順序編寫一個標簽的屬性,屬性的優(yōu)先級機制必須建立自己為了確保這將正常工作。

因此,所有的Thymeleaf屬性都定義了一個數(shù)字優(yōu)先級,它確定了它們在標記中執(zhí)行的順序。這個順序是:

Order

Feature

Attributes

1

Fragment inclusion

th:insert
th:replace

2

Fragment iteration

th:each

3

Conditional evaluation

th:if
th:unless
th:switch
th:case

4

Local variable definition

th:object
th:with

5

General attribute modification

th:attr
th:attrprepend
th:attrappend

6

Specific attribute modification

th:value
th:href
th:src
...

7

Text (tag body modification)

th:text
th:utext

8

Fragment specification

th:fragment

9

Fragment removal

th:remove

這種優(yōu)先機制意味著,如果屬性位置顛倒,上面的迭代片段將給出完全相同的結果(盡管它的可讀性稍微差一些):

11 注釋

11.1. 標志HTML/XML注釋

標準HTML/XML注釋<!——……——>可以在Thymeleaf模板的任何地方使用。這些評論中的任何內容都不會被Thymeleaf處理,并將逐字復制到結果:

<!-- User info follows --> <div th:text="${...}">... </div>

11.2. Thymeleaf 注釋模塊

解析器級別的注釋塊是在Thymeleaf解析模板時從模板中簡單刪除的代碼。它們是這樣的:

<!--/* This code will be removed at Thymeleaf parsing time! */-->

Thymeleaf將刪除<!——/**/——>,所以這些注釋塊也可以用于靜態(tài)打開模板時顯示代碼,知道當Thymeleaf處理它時,它會被刪除:

<!--/*--> <div>you can see me only before Thymeleaf processes me!</div> <!--*/-->

11.3. Thymeleaf原型注釋

Thymeleaf允許在靜態(tài)打開模板時(即作為原型)將特殊的注釋塊定義為注釋,但在執(zhí)行模板時將其視為普通標記。

<span>hello!</span> <!--/*/<div th:text="${...}">...</div> /*/--> <span>goodbye!</span>

Thymeleaf的解析系統(tǒng)將簡單地刪除<!—/*//*/—>標記,但不包括其內容,因此將不注釋這些內容。所以在執(zhí)行模板時,Thymeleaf會看到:

<span>hello!</span><div th:text="${...}">...</div><span>goodbye!</span>

與解析器級別的注釋塊一樣,這個特性與方言無關。

11.4.th:block

標準方言中包含的惟一元素處理器(不是屬性)th:block

block僅僅是一個屬性容器,它允許模板開發(fā)人員指定他們想要的任何屬性。Thymeleaf將執(zhí)行這些屬性,然后使塊(而不是其內容)消失。

因此,它可能很有用,例如,當創(chuàng)建迭代表時,每個元素需要一個以上的<tr>

<table><th:block th:each="user : ${users}"><tr><td th:text="${user.login}">...</td><td th:text="${user.name}">...</td></tr><tr><td colspan="2" th:text="${user.address}">...</td></tr></th:block> </table>

當與僅原型的注釋塊結合使用時尤其有用:

<table><!--/*/ <th:block th:each="user : ${users}"> /*/--><tr><td th:text="${user.login}">...</td><td th:text="${user.name}">...</td></tr><tr><td colspan="2" th:text="${user.address}">...</td></tr><!--/*/ </th:block> /*/--> </table>

注意這個解決方案是如何允許模板是有效的HTML(不需要在中添加禁止的

),并且在瀏覽器中作為原型靜態(tài)打開時仍然可以正常工作!

12 Thymeleaf嵌入

12.1 表達式嵌入

盡管標準方言允許我們使用標記屬性做幾乎所有的事情,但是在某些情況下,我們更喜歡直接將表達式寫入HTML文本。例如,我們可以這樣寫:

<p>Hello, [[${session.user.name}]]!</p>

……不是這樣的:

<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>

之間的表達式[[…]][(…)]Thymeleaf中被認為是內聯(lián)表達式,在它們內部,我們可以使用在th:textth:utext屬性中也有效的任何類型的表達式。

注意,然而[[……]]對應于th:text(即結果為html轉義)[(…)]對應于th:utext不執(zhí)行任何html轉義。因此,對于msg = '這是這樣的變量,很好!',給定這個片段:

<p>The message is "[(${msg})]"</p>

嵌入vs非嵌入

如果您來自以這種方式輸出文本為標準的其他模板引擎,您可能會問:為什么我們不從一開始就這樣做呢?它的代碼比所有那些th:text屬性都要少!

那么,在這里要小心,因為盡管您可能會發(fā)現(xiàn)內聯(lián)非常有趣,但是您應該始終記住,當靜態(tài)打開內聯(lián)表達式時,它們將在HTML文件中逐字顯示,所以您可能再也不能將它們用作設計原型了!

瀏覽器如何在不使用內聯(lián)的情況下靜態(tài)顯示我們的代碼片段……

Hello, Sebastian!
  • 并利用這
Hello, [[${session.user.name}]]!

就設計的實用性而言,……是相當清楚的。

禁止嵌入

但是可以禁用此機制,因為在某些情況下,我們確實希望輸出[[…]][(…)]序列,但不將其內容作為表達式處理。為此,我們將使用th:inline="none":

<p th:inline="none">A double array looks like this: [[1, 2, 3], [4, 5]]!</p>

12.2 文本嵌入

文本內聯(lián)非常類似于我們剛才看到的表達式內聯(lián)功能,但是它實際上增加了更多的功能。它必須顯式啟用th:inline="text"

文本內聯(lián)不僅允許我們使用我們剛才看到的相同的內聯(lián)表達式,而且實際上還可以像處理文本模板模式中的模板那樣處理標記體,這允許我們執(zhí)行基于文本的模板邏輯(不僅僅是輸出表達式)

我們將在下一章的文本模板模式中看到更多關于這一點的內容。

12.3 JavaScript 嵌入

JavaScript內聯(lián)允許在HTML模板模式下處理的模板中更好地集成JavaScript

與文本內聯(lián)一樣,這實際上相當于在JAVASCRIPT模板模式下像處理模板一樣處理腳本內容,因此文本模板模式的所有功能(參見下一章)就在手邊。然而,在本節(jié)中,我們將重點介紹如何使用它將Thymeleaf表達式的輸出添加到JavaScript塊中。

這個模式必須顯式啟用使用th:inline="javascript":

<script th:inline="javascript">...var username = [[${session.user.name}]];... </script>

這將導致

<script th:inline="javascript">...var username = "Sebastian \"Fruity\" Applejuice";... </script>

在上面的代碼中需要注意兩件重要的事情:

首先,JavaScript內聯(lián)不僅輸出所需的文本,還用引號和JavaScript—轉義其內容將表達式結果作為格式良好的JavaScript文本輸出。

其次,這是因為我們將${session.user.name}表達式作為轉義輸出,即使用一個雙尖括號表達式:[[${session.user.name}]]。如果我們使用unescape:

<script th:inline="javascript">...var username = [(${session.user.name})];... </script>

結果會是這樣的:

<script th:inline="javascript">...var username = Sebastian "Fruity" Applejuice;... </script>

這是格式錯誤的JavaScript代碼。但是,如果要通過追加內聯(lián)表達式來構建腳本的某些部分,輸出未轉義的內容可能是我們所需要的,所以手邊有這個工具是很好的。

JavaScript 非嵌入寫法

前面提到的JavaScript內聯(lián)機制的智能遠遠不止應用JavaScript特有的轉義和將表達式結果作為有效的文本輸出。

例如,我們可以將(轉義)內聯(lián)表達式包裝在JavaScript注釋中,如:

<script th:inline="javascript">...var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";... </script>

Thymeleaf會忽略我們在注釋后面和分號之前寫的所有東西(在本例中是“Gertrud Kiwifruit”),所以執(zhí)行這個的結果看起來就像我們沒有使用包裝注釋的時候:

<script th:inline="javascript">...var username = "Sebastian \"Fruity\" Applejuice";... </script>

但是再仔細看看原始模板代碼:

<script th:inline="javascript">...var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";... </script>

注意這是有效的JavaScript代碼。當您以靜態(tài)方式(而不是在服務器上執(zhí)行)打開模板文件時,它將完美地執(zhí)行。

所以我們這里有一種做JavaScript自然模板的方法!

嵌入求值和js序列號

關于JavaScript內聯(lián)需要注意的一件重要事情是,這個表達式的求值是智能的,不限于字符串。Thymeleaf將正確地用JavaScript語法編寫以下類型的對象:

  • Strings
  • Numbers
  • Booleans
  • Arrays
  • Collections
  • Maps
  • Beans (objects with?getter?and?setter?methods)

例如,如果我們有以下代碼:

<script th:inline="javascript">...var user = /*[[${session.user}]]*/ null;... </script>

$ {session.user}表達式將求值為user對象,Thymeleaf將其正確轉換為Javascript語法:

<script th:inline="javascript">...var user = {"age":null,"firstName":"John","lastName":"Apricot","name":"John Apricot","nationality":"Antarctica"};... </script>

這種JavaScript序列化的實現(xiàn)方式是通過org.thymeleaf.standard.serializer的實現(xiàn)實現(xiàn)的。IStandardJavaScriptSerializer接口,可以在模板引擎使用的標準方言的實例中配置該接口。

這個JS序列化機制的默認實現(xiàn)將在類路徑中查找Jackson庫,如果存在,將使用它。如果不是,它將應用一個內置的序列化機制,該機制覆蓋大多數(shù)場景的需求,并產生類似的結果(但靈活性較差)

12.4 CSS嵌入

Thymeleaf還允許在CSS

<style th:inline="css">... </style>

例如,我們有兩個變量被設置為兩個不同的字符串值:

classname = 'main elems' align = 'center'

我們可以這樣使用它們:

<style th:inline="css">.[[${classname}]] {text-align: [[${align}]];} </style>

結果會是:

<style th:inline="css">.main\ elems {text-align: center;} </style>

注意CSS內聯(lián)也有一些智能,就像JavaScript一樣。具體地說,通過轉義表達式([[${classname}]])輸出的表達式將轉義為CSS標識符。這就是為什么我們的classname = 'main elems'在上面的代碼片段中變成了main\ elems

高級特性

與之前對javascript的解釋類似,CSS內聯(lián)還允許我們的<style>標記靜態(tài)和動態(tài)地工作,即通過將內聯(lián)表達式包裝在注釋中作為CSS自然模板。見:

<style th:inline="css">.main\ elems {text-align: /*[[${align}]]*/ left;} </style>

13 文本模式

13.1 文本語法

三種Thymeleaf模板模式被認為是文本模式:文本、JAVASCRIPTCSS。這將它們與標記模板模式(HTMLXML)區(qū)分開來。

文本模板模式和標記模式之間的關鍵區(qū)別在于,在文本模板中沒有以屬性形式插入邏輯的標記,因此我們必須依賴其他機制。

第一個也是最基本的機制是內聯(lián),我們在上一章已經詳細介紹過了。內聯(lián)語法是在文本模板模式下輸出表達式結果的最簡單方法,因此這是一個非常有效的文本電子郵件模板。

Dear [(${name})],Please find attached the results of the report you requestedwith name "[(${report.name})]".Sincerely,The Reporter.

即使沒有標記,上面的示例也是一個完整而有效的Thymeleaf模板,可以在文本模板模式下執(zhí)行。

但是為了包含比輸出表達式更復雜的邏輯,我們需要一種新的非基于標記的語法:

[# th:each="item : ${items}"]- [(${item})] [/]

這實際上是更詳細的壓縮版本:

[#th:block th:each="item : ${items}"]- [#th:block th:utext="${item}" /] [/th:block]

注意這個新語法是如何基于聲明為[#element…而不是。元素是開放的,比如[#元素…]和像[/element]一樣關閉的標簽,獨立標簽可以通過使用/最小化打開的元素來聲明,其方式幾乎等同于XML標簽:[#element…/)。

?

標準方言只包含這些元素之一的處理器:已知的th:塊,盡管我們可以在方言中擴展它并以通常的方式創(chuàng)建新元素。另外,第th:block元素([#th:block…]))……[/th:block])可以縮寫為空字符串([#…)……[/]),所以上面的block實際上等于:

[# th:each="item : ${items}"]- [# th:utext="${item}" /] [/]

假設[# th:utext="${item}" /]相當于一個內聯(lián)的未轉義表達式,我們可以使用它來減少代碼。這樣我們就得到了上面看到的第一個代碼片段:

[# th:each="item : ${items}"]- [(${item})] [/]

注意,文本語法需要完全的元素平衡(沒有未關閉的標記)和引號屬性——它更像xml風格,而不是html風格。

讓我們來看看一個更完整的文本模板的例子,一個純文本電子郵件模板:

Dear [(${customer.name})],This is the list of our products:[# th:each="prod : ${products}"]- [(${prod.name})]. Price: [(${prod.price})] EUR/kg [/]Thanks,The Thymeleaf Shop

執(zhí)行之后,結果可能是這樣的:

Dear Mary Ann Blueberry,This is the list of our products:- Apricots. Price: 1.12 EUR/kg- Bananas. Price: 1.78 EUR/kg- Apples. Price: 0.85 EUR/kg- Watermelon. Price: 1.91 EUR/kgThanks,The Thymeleaf Shop

另一個JAVASCRIPT模板模式的例子是一個greeting .js文件,我們將其作為文本模板處理,并從HTML頁面調用結果。注意,這不是HTML模板中的

var greeter = function() {var username = [[${session.user.name}]];[# th:each="salut : ${salutations}"] alert([[${salut}]] + " " + username);[/]};

執(zhí)行之后,結果可能是這樣的:

var greeter = function() {var username = "Bertrand \"Crunchy\" Pear";alert("Hello" + " " + username);alert("Ol\u00E1" + " " + username);alert("Hola" + " " + username);};

忽略元素屬性

為了避免與可能以其他模式(例如HTML模板中的文本模式內聯(lián))處理的模板部分進行交互,Thymeleaf 3.0允許轉義文本語法中元素的屬性。所以:

  • Attributes in?TEXT?template mode will be?HTML-unescaped.
  • Attributes in?JAVASCRIPT?template mode will be?JavaScript-unescaped.
  • Attributes in?CSS?template mode will be?CSS-unescaped.

所以這在文本模式模板中是完全可以的(注意>):

[# th:if="${120&lt;user.age}"]Congratulations![/]

當然了。這在實際的文本模板中是沒有意義的,但是如果我們使用th:inline="text"塊處理HTML模板(其中包含上述代碼),并且希望確保瀏覽器不接受

13.2擴展

這種語法的優(yōu)點之一是它與標記語法一樣具有可擴展性。開發(fā)人員仍然可以使用自定義元素和屬性定義自己的方言,對它們應用前綴(可選),然后在文本模板模式中使用它們:

[#myorg:dosomething myorg:importantattr="211"]some text[/myorg:dosomething]

13.3 原型注釋

JAVASCRIPT和CSS模板模式(文本不可用)允許在特殊的注釋語法/*[+…+]*/使Thymeleaf在處理模板時自動取消注釋:

var x = 23;/*[+var msg = "This is a working application";+]*/var f = function() {...

13.4 文本解析器級注釋塊:刪除代碼

與僅使用原型的注釋塊類似,所有三種文本模板模式(文本、JAVASCRIPTCSS)都可以指示Thymeleaf刪除特殊/*[- *//* -]*/標記之間的代碼,如下所示:

13.5 JavaScript and CSS 模板

如上一章所述,JavaScriptCSS內聯(lián)提供了在JavaScript/CSS注釋中包含內聯(lián)表達式的可能性,例如:

這是有效的JavaScript,一旦執(zhí)行可能像:

... var username = "John Apricot"; ...

在注釋中封裝內聯(lián)表達式的相同技巧實際上可以用于整個文本模式語法:

/*[# th:if="${user.admin}"]*/alert('Welcome admin');/*[/]*/

當模板靜態(tài)打開時(因為它是100%有效的JavaScript),以及當模板運行時(如果用戶是管理員),上面代碼中的警告將顯示出來。它等于:

[# th:if="${user.admin}"]alert('Welcome admin');[/]

這實際上是在模板解析期間將初始版本轉換為的代碼。

但是請注意,在注釋中包裝元素并不會清除它們所在的行(a;如內聯(lián)輸出表達式所做的那樣。這種行為只保留給內聯(lián)輸出表達式。

因此,Thymeleaf 3.0允許以自然模板的形式開發(fā)復雜的JavaScript腳本和CSS樣式表,作為原型和工作模板都有效。

14雜貨店(略)

15 配置

15.1 模板解析

為了我們的Thymes虛擬雜貨店,我們選擇了一個名為servletcontextITemplateResolver實現(xiàn),它允許我們從Servlet上下文中獲取模板作為資源。

除了讓我們能夠通過實現(xiàn)ITemplateResolver來創(chuàng)建自己的模板解析器之外,Thymeleaf還提供了四個開箱即用的實現(xiàn):

org.thymeleaf.templateresolverClassLoaderTemplateResolver,它將模板解析為類加載器資源,如:

return Thread.currentThread().getContextClassLoader().getResourceAsStream(template);

org.thymeleaf.templateresolverFileTemplateResolver,它將模板解析為文件系統(tǒng)中的文件,如:

return new FileInputStream(new File(template));

org.thymeleaf.templateresolverUrlTemplateResolver,它將模板解析為url(甚至是非本地的),比如:

return (new URL(template)).openStream();

org.thymeleaf.templateresolverStringTemplateResolver,它直接將模板解析為指定為模板的字符串(或模板名稱,在本例中顯然不僅僅是一個名稱):

return new StringReader(templateName);

ITemplateResolver的所有預綁定實現(xiàn)都允許相同的一組配置參數(shù),其中包括:

templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html");

允許使用與文件名不直接對應的模板名稱的emplate別名。如果前綴/前綴和別名都存在,則在前綴/后綴之前應用別名:

templateResolver.addTemplateAlias("adminHome","profiles/admin/home"); templateResolver.setTemplateAliases(aliasesMap);

讀取模板時應用的編碼:

templateResolver.setEncoding("UTF-8");

使用的模板模式:

// Default is HTML templateResolver.setTemplateMode("XML");

模板緩存的默認模式,以及定義特定模板是否可緩存的模式:

// Default is true templateResolver.setCacheable(false); templateResolver.getCacheablePatternSpec().addPattern("/users/*");

TTL(以毫秒為單位)表示源自此模板解析器的已解析模板緩存項。如果沒有設置,從緩存中刪除條目的唯一方法是超過緩存的最大大小(最老的條目將被刪除)

? // Default is no TTL (only cache size exceeded would remove entries) templateResolver.setCacheTTLMs(60000L);

?

Thymeleaf + Spring集成包提供了一個SpringResourceTemplateResolver實現(xiàn),它使用所有Spring基礎設施來訪問和讀取應用程序中的資源,這是支持Spring的應用程序中推薦的實現(xiàn)。

?

模板解析器鏈

此外,模板引擎可以指定多個模板解析器,在這種情況下,可以在它們之間建立一個模板解析順序,這樣,如果第一個解析器無法解析模板,就會請求第二個解析器,以此類推:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver(); classLoaderTemplateResolver.setOrder(Integer.valueOf(1));ServletContextTemplateResolver servletContextTemplateResolver = new ServletContextTemplateResolver(servletContext); servletContextTemplateResolver.setOrder(Integer.valueOf(2));templateEngine.addTemplateResolver(classLoaderTemplateResolver); templateEngine.addTemplateResolver(servletContextTemplateResolver);

當應用多個模板解析器時,建議為每個模板解析器指定模式,以便Thymeleaf能夠快速丟棄那些不打算解析模板的模板解析器,從而提高性能。這樣做不是一種要求,而是一種建議:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver(); classLoaderTemplateResolver.setOrder(Integer.valueOf(1)); // This classloader will not be even asked for any templates not matching these patterns classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/layout/*.html"); classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/menu/*.html");ServletContextTemplateResolver servletContextTemplateResolver = new ServletContextTemplateResolver(servletContext); servletContextTemplateResolver.setOrder(Integer.valueOf(2));

如果沒有指定這些可解析模式,我們將依賴于所使用的ITemplateResolver實現(xiàn)的特定功能。請注意,并不是所有的實現(xiàn)都能夠在解析之前確定模板的存在,因此總是可以認為模板是可解析的,并打破解析鏈(不允許其他解析器檢查相同的模板),但隨后無法讀取真正的資源。

所有包含在核心Thymeleaf中的ITemplateResolver實現(xiàn)都包含一種機制,它允許我們在考慮可解析資源之前,讓解析器真正檢查資源是否存在。它是checkExistence標志,工作方式如下:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver(); classLoaderTemplateResolver.setOrder(Integer.valueOf(1)); classLoaderTempalteResolver.setCheckExistence(true);

這個checkExistence標志強制解析器在解析階段對資源存在性執(zhí)行真正的檢查(如果exist check返回false,則調用鏈中的以下解析器)。雖然這聽起來可能在所有情況下,在大多數(shù)情況下,這將意味著雙重訪問資源本身(一次檢查存在,另一個時間閱讀),和可能是一個性能問題在某些場景中,例如遠程url模板資源——一個潛在的性能問題,可能無論如何被使用在很大程度上減輕模板緩存(在這種情況下, ,模板只能解決他們第一次訪問)

15.2 消息解析器

我們沒有為雜貨應用程序顯式地指定消息解析器實現(xiàn),正如前面所解釋的,這意味著所使用的實現(xiàn)是一個org.thymeleaf.messageresolverStandardMessageResolver對象。

StandardMessageResolverIMessageResolver接口的標準實現(xiàn),但是我們可以根據(jù)應用程序的特定需求創(chuàng)建自己的接口。

默認情況下,Thymeleaf + Spring集成包提供一個IMessageResolver實現(xiàn),它使用標準的Spring方式檢索外部化的消息,方法是使用在Spring應用程序上下文中聲明的MessageSource bean

標準消息解析器

那么StandardMessageResolver如何查找特定模板中請求的消息呢?

如果模板名稱是home并且位于/WEB-INF/templates/home中。,而請求的語言環(huán)境是gl_ES,那么這個解析器將在以下文件中查找消息,順序如下:

  • /WEB-INF/templates/home_gl_ES.properties
  • /WEB-INF/templates/home_gl.properties
  • /WEB-INF/templates/home.properties

有關完整的消息解析機制如何工作的詳細信息,請參閱StandardMessageResolver類的JavaDoc文檔。

配置消息解析器

如果我們想向模板引擎中添加消息解析器(或更多),該怎么辦?容易:

// For setting only one templateEngine.setMessageResolver(messageResolver);// For setting more than one templateEngine.addMessageResolver(messageResolver);

為什么我們希望有多個消息解析器?與模板解析器的原因相同:消息解析器是有序的,如果第一個解析器不能解析特定的消息,將詢問第二個解析器,然后詢問第三個解析器,等等。

15.3 轉換服務

使我們能夠通過雙大括號語法(${{…}})執(zhí)行數(shù)據(jù)轉換和格式化操作的轉換服務實際上是標準方言的一個特性,而不是Thymeleaf模板引擎本身的特性。

因此,配置它的方法是將IStandardConversionService接口的自定義實現(xiàn)直接設置到正在配置到模板引擎中的StandardDialect實例中。如:

? IStandardConversionService customConversionService = ...StandardDialect dialect = new StandardDialect(); dialect.setConversionService(customConversionService);templateEngine.setDialect(dialect);

?

請注意,Thymeleaf -spring3Thymeleaf -spring4包包含SpringStandardDialect,并且該方言已經預先配置了IStandardConversionService的實現(xiàn),該實現(xiàn)將Spring自己的轉換服務基礎結構集成到Thymeleaf中。

?

15.4 日志

Thymeleaf非常關注日志記錄,并且總是試圖通過它的日志接口提供最大數(shù)量的有用信息。

所使用的日志庫是slf4j,它實際上充當?shù)轿覀兛赡芟M趹贸绦蛑惺褂玫娜魏稳罩緦崿F(xiàn)(例如log4j)的橋梁。

Thymeleaf類將記錄跟蹤、調試和信息級別的信息,這取決于我們想要的詳細程度,除了一般的日志記錄,它還將使用三個與TemplateEngine類相關聯(lián)的特殊日志記錄器,我們可以為不同的目的分別配置它們:

  • org.thymeleaf.TemplateEngine.CONFIG?will output detailed configuration of the library during initialization.
  • org.thymeleaf.TemplateEngine.TIMER?will output information about the amount of time taken to process each template (useful for benchmarking!)
  • org.thymeleaf.TemplateEngine.cache?is the prefix for a set of loggers that output specific information about the caches. Although the names of the cache loggers are configurable by the user and thus could change, by default they are:
    • org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE
    • org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE

使用log4jThymeleaf日志記錄基礎結構的一個示例配置可以是:

log4j.logger.org.thymeleaf=DEBUG log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=TRACE log4j.logger.org.thymeleaf.TemplateEngine.TIMER=TRACE log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=TRACE

16 模板緩存

Thymeleaf工作由于一組解析器——標記和文本模板解析成的事件序列(開放標簽、文本結束標記、評論等)和一系列的處理器——一個為每個類型的行為,需要應用,修改模板解析事件序列為了創(chuàng)建我們期待的結果。通過結合原始模板與我們的數(shù)據(jù)。

默認情況下,它還包括存儲已解析模板的緩存;在處理模板文件之前讀取和解析模板文件所產生的事件序列。這在web應用程序中尤其有用,它基于以下概念:

17 離線模板

17.1 離線模板的概念(分離thymeleafdom

到目前為止,我們在雜貨店中使用的模板都是按照通常的方式完成的,將邏輯以屬性的形式插入到模板中。

但是,Thymeleaf還允許我們將模板標記與其邏輯完全解耦,允許在HTMLXML模板模式中創(chuàng)建完全沒有邏輯的標記模板。

其主要思想是模板邏輯將在一個單獨的邏輯文件中定義(更確切地說,是一個邏輯資源,因為它不需要是一個文件)。默認情況下,該邏輯資源將是與模板文件位于相同位置(例如文件夾)的另一個文件,具有相同的名稱,但是.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或模板知識的設計人員可以創(chuàng)建、編輯和/或理解的模板文件。或者由完全沒有Thymeleaf鉤子的外部系統(tǒng)提供的HTML片段。

現(xiàn)在讓我們通過創(chuàng)建額外的home.th.xml文件將home.html模板轉換為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塊中看到許多標記。這些標記通過它們的sel屬性對原始模板的節(jié)點執(zhí)行屬性注入,這些sel屬性包含Thymeleaf標記選擇器(實際上是AttoParser標記選擇器)。

還請注意,標記可以嵌套,以便追加它們的選擇器。例如,上面的sel="/tr[0]"將被處理為sel="#usersTable/tr[0]"。用戶名的選擇器將被處理為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>

這看起來更熟悉,而且實際上比創(chuàng)建兩個單獨的文件更簡單。但是解耦模板的優(yōu)點是,我們可以使模板完全獨立于Thymeleaf,因此從設計的角度來看,具有更好的可維護性。

當然,設計人員或開發(fā)人員之間仍然需要一些契約例如,用戶需要id="usersTable"—但是在許多場景中,純html模板將是設計和開發(fā)團隊之間更好的通信工件。

17.2 配置離線模板

開啟離線模板

默認情況下,不會期望每個模板都具有解耦邏輯。相反,配置的模板解析器(ITemplateResolver的實現(xiàn))將需要專門將它們解析的模板標記為使用解耦邏輯。

除了StringTemplateResolver(它不支持解耦邏輯)之外,ITemplateResolver的所有其他開箱即用實現(xiàn)都將提供一個名為usedeconpledlogic的標志,該標志將把該解耦器解析的所有模板標記為所有或部分邏輯都可能位于單獨的資源中:

final ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext); ... templateResolver.setUseDecoupledLogic(true);

混用在線/離線模板

在啟用時,不需要解耦模板邏輯。當啟用時,這意味著引擎將查找包含解耦邏輯的資源,如果它存在,則解析它并將其與原始模板合并。如果解耦邏輯資源不存在,則不會拋出錯誤。

同樣,在同一個模板中,我們可以混合耦合和解耦邏輯,例如在原始模板文件中添加一些Thymeleaf屬性,而將其他屬性留給單獨的解耦邏輯文件。最常見的情況是使用新的th:ref屬性(v3.0)

17.3 ref屬性

ref只是一個標記屬性。從處理的角度來看,它什么也不做,只是在處理模板時消失,但它的有用之處在于它作為標記引用,即它可以像標記名稱或片段(th:fragment)一樣通過標記選擇器中的名稱解析。

如果我們有一個選擇器

<attr sel="whatever" .../>

這將匹配:

  • Any?<whatever>?tags.
  • Any tags with a?th:fragment="whatever"?attribute.
  • Any tags with a?th:ref="whatever"?attribute.

例如,使用純html id屬性th:ref的優(yōu)點是什么?僅僅是因為我們可能不希望向標記中添加這么多id和類屬性來充當邏輯錨,這最終可能會污染我們的輸出。

同樣的道理,th:ref的缺點是什么?顯然,我們需要在模板中添加一些Thymeleaf邏輯(“邏輯”)

請注意th:ref屬性的這種適用性不僅適用于解耦的邏輯模板文件:它在其他類型的場景中也同樣適用,比如片段表達式(~{…})

17.4 離線模板的性能

影響非常小。當一個已解析的模板被標記為使用解耦邏輯而沒有被緩存時,模板邏輯資源將首先被解析、解析并處理為內存中指令的安全:基本上是要注入到每個標記選擇器中的屬性列表。

但這是惟一需要的額外步驟,因為在此之后,將解析實際的模板,在解析模板時,解析器本身將動態(tài)地注入這些屬性,這要感謝AttoParser中的節(jié)點選擇高級功能。因此,解析后的節(jié)點將從解析器中出來,就好像它們的注入屬性寫在原始模板文件中一樣。

這最大的好處是什么?當將模板配置為緩存時,它將被緩存,其中已經包含注入的屬性。因此,對可緩存模板使用解耦模板的開銷,一旦它們被緩存,將絕對為零。

17.5 何時分離邏輯代碼和樣式(離線模板)

Thymeleaf解析與每個模板對應的解耦邏輯資源的方式可由用戶配置。它由一個擴展點確定

ideconpledtemplatelogicresolver,它提供了一個默認的實現(xiàn):standarddeconpledtemplatelogicresolver

這個標準實現(xiàn)做什么?

首先,它對模板資源的基名稱應用前綴和后綴(通過ITemplateResource#getBaseName()方法獲得)。前綴和后綴都可以配置,默認情況下,前綴為空,后綴為.th.xml

其次,它要求模板資源通過ITemplateResource#relative(String relativeLocation)方法解析具有計算名稱的相對資源。

使用的ideconpledtemplatelogicresolver的具體實現(xiàn)可以在TemplateEngine上輕松配置:

final StandardDecoupledTemplateLogicResolver decoupledresolver = new StandardDecoupledTemplateLogicResolver(); decoupledResolver.setPrefix("../viewlogic/"); ... templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);

?

?

?

?

總結

以上是生活随笔為你收集整理的fundamentals\java\Thymeleaf的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

日本中文乱码卡一卡二新区 | 亚洲精品在线观看视频 | 亚州性色| 丝袜制服综合网 | 91精品免费在线视频 | 精品欧美乱码久久久久久 | 国产一级特黄毛片在线毛片 | 在线视频一区观看 | 操久 | 人人干97| 天天摸日日摸人人看 | 久久亚洲影院 | 韩日av在线 | 久久综合狠狠综合 | 综合色综合色 | 2022中文字幕在线观看 | av中文国产 | 色综合亚洲精品激情狠狠 | 丁香六月网 | 亚洲免费成人 | 亚洲日本一区二区在线 | 国产精品女主播一区二区三区 | 中文字幕在线观看免费观看 | 欧美极度另类 | 公与妇乱理三级xxx 在线观看视频在线观看 | 国产免费久久av | 黄色片免费在线 | 久久久黄色免费网站 | 免费久久99精品国产婷婷六月 | av中文字幕日韩 | 国产视频1| 天天综合网天天综合色 | 91成人免费电影 | 国产精品久久久久久久av电影 | 欧美网址在线观看 | 久久精品网| 国际精品久久久久 | 国产精品三级视频 | 久久综合九色综合欧美狠狠 | 成人av网站在线观看 | 黄色小网站免费看 | 国产高清av | 色av男人的天堂免费在线 | 麻豆一级视频 | 337p日本欧洲亚洲大胆裸体艺术 | 国产日韩在线观看一区 | 日韩一级成人av | 丁香五婷 | 国产精品毛片久久 | 午夜色性片 | 96精品高清视频在线观看软件特色 | 国产精品嫩草影院9 | 五月天狠狠操 | 久久综合九色综合欧美就去吻 | 国产精品精品久久久久久 | 天天爽夜夜爽精品视频婷婷 | 摸bbb搡bbb搡bbbb | 亚洲午夜久久久久久久久电影网 | 99久久精品午夜一区二区小说 | 五月激情久久 | 免费黄色av.| 91大神dom调教在线观看 | 国产精品久久久av久久久 | 精品日本视频 | 三级av黄色 | 久久精品成人 | 免费在线国产 | 91精品啪在线观看国产 | 国产成人一区二区三区影院在线 | 亚洲最大色| 国产护士在线 | 国产精品久久久久av免费 | 成年人在线观看网站 | 久热电影 | 99精品国产99久久久久久97 | 五月天色中色 | 亚洲永久精品国产 | 韩国av一区二区三区 | 九九免费观看视频 | 日黄网站 | 国产性xxxx| 去干成人网 | 亚洲成人一区 | 日韩精品专区在线影院重磅 | 婷婷五情天综123 | 精品视频资源站 | 视频在线99 | 免费日韩一区二区三区 | 欧美日韩国产综合网 | 丁香高清视频在线看看 | 国产成人精品网站 | 日韩精品久久久久久中文字幕8 | 色综合网| 天天天天天天天操 | 欧美性极品xxxx娇小 | 国产在线观看免 | 国产男女爽爽爽免费视频 | 欧美日韩久久久 | 97碰碰碰| 欧美日韩在线观看一区二区三区 | 超级碰碰碰视频 | 国产精品麻豆果冻传媒在线播放 | 中文视频在线播放 | 四虎在线免费观看视频 | 日日干网址 | 久热av在线 | 91精品中文字幕 | 国产小视频国产精品 | 人人爽人人澡 | 在线观看亚洲电影 | 久久香蕉一区 | 九九视频免费在线观看 | 99久久99久久综合 | 国产成人av网址 | 日韩欧美综合视频 | 天天综合91| 91av在线免费看 | 免费在线视频一区二区 | 亚洲精品视频在 | 在线黄色免费 | 日韩精品一区二区在线观看 | 亚洲六月丁香色婷婷综合久久 | 91香蕉国产在线观看软件 | www.五月天激情| 最近日本韩国中文字幕 | 久久精品站 | 精品国模一区二区 | 精品久久国产一区 | 美女一级毛片视频 | 天天艹天天 | av在线播放观看 | 成人国产精品免费观看 | 亚洲最新在线 | 天天射天天爽 | 国产精品原创av片国产免费 | 丁香花在线视频观看免费 | 日韩中文字幕在线观看 | 国产女人免费看a级丨片 | 国产色网站 | 国产一二区视频 | 国产拍揄自揄精品视频麻豆 | 国产精品资源在线观看 | 中文字幕亚洲情99在线 | 久久公开免费视频 | 日韩电影在线观看一区二区 | 国产黄影院色大全免费 | 国产精品免费在线播放 | 日韩成年视频 | 亚洲激情 欧美激情 | 久久久精品成人 | 粉嫩一区二区三区粉嫩91 | 97夜夜澡人人爽人人免费 | 亚洲国产欧美一区二区三区丁香婷 | 欧美孕妇与黑人孕交 | 五月婷婷六月丁香 | 国产精品门事件 | 久草在在线| 午夜视频一区二区三区 | 久久99久国产精品黄毛片入口 | 日韩免费不卡av | 丁香六月激情婷婷 | 国产精品成人在线 | 色网站视频 | 高清av免费观看 | 欧美性天天 | 国产h在线观看 | 精品国产网址 | 欧美aa在线 | 日韩三级在线 | 国产精品18久久久久久久久久久久 | 久久精品成人热国产成 | 亚洲欧美综合精品久久成人 | 亚洲精品乱码白浆高清久久久久久 | 久草在线视频看看 | 在线观看亚洲专区 | 视频三区 | 欧美精品久久久久久久久免 | 五月天中文在线 | av免费成人 | 国产午夜在线 | 日韩成人看片 | 狠狠gao | 久视频在线 | 亚洲精品成人av在线 | 久久精选 | 国产免费观看久久 | 最新av在线播放 | 欧美综合久久久 | 亚洲日本精品视频 | 久久一区二区三区日韩 | 黄色大片视频网站 | 91精品国产电影 | 久久综合毛片 | 日韩久久精品一区二区三区下载 | 99久久999久久久精玫瑰 | 欧美一二三区在线播放 | 日韩在线观看你懂得 | 国产青青青 | 日韩欧在线 | 精品国精品自拍自在线 | 亚洲免费a | 久久国产午夜精品理论片最新版本 | 国产精品久久一卡二卡 | 麻豆91在线观看 | 久久视频在线观看免费 | 久久99精品国产麻豆宅宅 | 不卡电影免费在线播放一区 | 五月婷婷综合在线观看 | 久久av中文字幕片 | 国产一区二区三区免费观看视频 | av免费看av | 日韩91在线 | 久草视频观看 | 亚洲精品午夜aaa久久久 | 成人蜜桃视频 | 九九99 | 深夜免费福利网站 | 97国产情侣爱久久免费观看 | 成人国产电影在线观看 | 人人澡人人干 | 91尤物国产尤物福利在线播放 | 国产第一页在线播放 | 91在线视频免费观看 | 91精品国产92久久久久 | 91视频在线 | 精品1区二区 | 国产一区电影在线观看 | 亚洲一区黄色 | 综合天堂av久久久久久久 | 91人人网 | 91精品黄色| 97视频入口免费观看 | 超碰97在线人人 | 日韩高清一区 | 日韩精品视频在线观看网址 | av福利在线看 | 黄色影院在线免费观看 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 日日爱网址 | 国产高清无av久久 | 日韩av二区 | 中文字幕在线观看资源 | 久久精品一区二区三 | 久久成人亚洲欧美电影 | 久久少妇av | 激情文学综合丁香 | 亚洲最大av网 | av一级久久 | 欧美精品一区二区蜜臀亚洲 | www.黄色在线 | 久久伊人爱 | 日本91在线 | 青春草免费视频 | 久久久网页 | 国产资源精品 | 99久久精品国产亚洲 | a在线视频v视频 | 亚洲永久国产精品 | 91视视频在线直接观看在线看网页在线看 | 亚洲 欧美 另类人妖 | 欧美一级片在线免费观看 | 国产手机av在线 | 成人看片| 久久久麻豆视频 | 日韩亚洲在线视频 | 日韩精品免费在线观看 | 99热亚洲精品 | 蜜臀久久99精品久久久无需会员 | 色噜噜狠狠色综合中国 | av一本久道久久波多野结衣 | 国产一区高清在线观看 | 久久av免费| 免费看毛片网站 | 久久爱综合 | 国产在线观看xxx | 久久天天躁夜夜躁狠狠躁2022 | 狠狠干夜夜操天天爽 | 欧美日韩一区二区三区在线观看视频 | 一级片免费在线 | 亚洲美女免费精品视频在线观看 | 天堂av在线免费 | 亚洲精品一区二区精华 | av在线成人 | 粉嫩av一区二区三区四区在线观看 | 色综合婷婷久久 | 狠狠做深爱婷婷综合一区 | 91超碰在线播放 | 国产九九九视频 | 在线播放国产一区二区三区 | 中文字幕在线视频网站 | 久久精品99国产精品亚洲最刺激 | 国产精品99久久久久久大便 | 久久久91精品国产一区二区精品 | 91视频在线免费看 | 综合网成人 | 男女日麻批 | 午夜神马福利 | 91麻豆精品国产91久久久无需广告 | 丁香九月婷婷综合 | 福利视频一二区 | 一区在线电影 | 久草在线最新免费 | 久久精品官网 | 国产日韩亚洲 | 免费三级在线 | 波多野结衣最新 | 日日噜噜噜噜夜夜爽亚洲精品 | 视频国产在线观看18 | 日韩美在线 | www.天天射.com| 九色最新网址 | 色综合天天天天做夜夜夜夜做 | 国产福利资源 | 久久99久| 亚洲精品中文在线 | 一区二区三区免费在线观看视频 | 99久久精品国产欧美主题曲 | 久久精品婷婷 | 成人高清在线观看 | 亚洲春色综合另类校园电影 | 97精品视频在线 | 天天操天天操天天操天天操天天操 | 久久99国产精品二区护士 | 青草视频在线 | 久久美女视频 | 免费精品久久久 | 中文字幕欧美日韩va免费视频 | 日韩超碰| 精品国产一区二区三区四区vr | 国产精品久久久久久久久费观看 | 国产自产在线视频 | 中文字幕免费在线看 | 免费人做人爱www的视 | 色网影音先锋 | 久久99精品国产91久久来源 | 欧美成人在线免费观看 | 99热.com| 色香蕉视频 | 激情黄色av| 国产96在线观看 | 亚洲电影一区二区 | 日日夜夜精品免费视频 | 日韩av免费一区二区 | 五月花婷婷 | 欧美污污视频 | 久久久久人人 | 人人澡人人模 | 免费福利片| 日韩精品欧美一区 | 国产一二三在线视频 | 国产69熟| 日本字幕网 | 99视频精品免费视频 | 国产日韩精品在线观看 | 五月婷久久| 91麻豆视频 | 国产亚洲小视频 | 久久夜视频 | 91精品黄色 | 国产在线视频资源 | 最近日本中文字幕 | 久久人人爽人人爽人人片 | 欧美性黄网官网 | 狠狠色噜噜狠狠狠 | 日韩美女一级片 | 中文字幕有码在线观看 | 99精品欧美一区二区三区黑人哦 | 日韩在线免费播放 | 永久黄网站色视频免费观看w | 在线免费看黄色 | 夜夜操狠狠干 | 国产一区二区三精品久久久无广告 | 伊人色综合网 | 插插插色综合 | 日韩激情在线视频 | 中文字幕在线看视频国产中文版 | 激情一区二区三区欧美 | 日日操操操 | 亚洲免费国产视频 | 久久成人国产精品入口 | 亚洲视频播放 | 人人艹视频 | 国产日韩视频在线 | 在线观看欧美成人 | 成人蜜桃 | 久久福利 | 三上悠亚一区二区在线观看 | 日韩在线视频观看免费 | 精品美女久久久久久免费 | 精品国产一区二区三区四区在线观看 | 久久免费视频在线观看 | 久久精品视频在线播放 | 亚洲高清在线精品 | 亚洲精品合集 | 日韩一区二区三区在线看 | 久久精品视频免费播放 | 国产99一区 | 成年人网站免费观看 | www.91av在线 | 国产在线观看午夜 | 91禁在线看 | 亚洲精品免费在线 | 免费视频xnxx com | 国产又黄又爽无遮挡 | 日韩在线网 | 国产精品一区二区久久 | 在线久草视频 | 一区二区欧美激情 | 在线观看深夜福利 | 亚洲天堂在线观看完整版 | 国产自产高清不卡 | 日日爱999 | 超碰97免费在线 | 久久一久久 | 久久99久国产精品黄毛片入口 | 最新高清无码专区 | 狠狠夜夜 | 丁香久久激情 | 97在线精品 | 欧美日韩一二三四区 | 久久老司机精品视频 | 久草久草久草久草 | 最近最新最好看中文视频 | 国产精品九九九九九 | 欧美日韩中 | 视频国产在线观看18 | 午夜私人影院久久久久 | 国产精品一区二区久久久 | 久久精品影视 | 日韩在线资源 | 亚洲爱视频 | 成人国产网址 | 日韩电影一区二区在线观看 | 国产成人精品亚洲精品 | 久久无码av一区二区三区电影网 | 麻豆免费在线播放 | 91在线国内视频 | 久久九九网站 | 日韩91av | 午夜av日韩 | 国产精品一二三 | 久久久国产毛片 | 国产在线国偷精品产拍免费yy | 国产精品九九久久99视频 | 免费黄色在线 | 日本久久中文 | 亚洲一区av | 91网址在线观看 | 五月激情电影 | 日韩精品三区四区 | 精品视频久久久久久 | 欧美日韩亚洲在线 | 精品黄色片 | 亚洲免费一级电影 | 91视频啪 | 综合网久久 | 草久在线 | 2024国产在线 | 亚洲精品国产欧美在线观看 | www久久国产| 日韩精品一区二区三区中文字幕 | www亚洲一区 | 深爱激情五月婷婷 | 日韩精品观看 | 成人在线小视频 | 黄污在线看 | 国产成人精品在线观看 | 精品v亚洲v欧美v高清v | 精品国产乱码一区二区三区在线 | 免费视频色 | 国产精品久久久久免费 | 国产成人精品一区二区在线 | 一区二区三区在线观看免费 | 欧美日本一区 | 婷婷中文字幕综合 | 国产麻豆视频免费观看 | 日韩av有码在线 | 又黄又爽又湿又无遮挡的在线视频 | 亚洲最新在线 | 超碰97网站 | 免费视频你懂的 | 久久精品国产成人 | 日韩中文字幕免费电影 | 激情久久久久久久久久久久久久久久 | 久久久91精品国产一区二区三区 | 性色av一区二区三区在线观看 | 欧美激情综合五月 | 777xxx欧美| 日韩在线观看三区 | 亚洲欧美在线视频免费 | 欧美孕交vivoestv另类 | 99riav1国产精品视频 | 日韩精品欧美专区 | 99人久久精品视频最新地址 | 国产精品久久久久久久久久久久午夜 | 奇米网444| 在线观看黄色免费视频 | 亚洲天堂色婷婷 | www.91国产 | 日韩黄色在线电影 | 中文字幕日本在线观看 | 国产91精品看黄网站 | 91在线中文| 999国内精品永久免费视频 | 天天天综合 | 麻豆视频免费版 | 国产精品久久久久久久久久久久午夜片 | 国产免费三级在线观看 | 三级黄色片子 | 91精品秘密在线观看 | 成人h视频在线播放 | 国内精品久久久久国产 | av成人免费在线看 | 久操视频在线播放 | 成人av手机在线 | 天天射天天爱天天干 | 亚洲精品在线观看视频 | 免费中午字幕无吗 | 色天天中文 | 黄色网址中文字幕 | 五月婷婷亚洲 | 亚洲女人天堂成人av在线 | 午夜久久久久 | 久久刺激视频 | 在线免费黄色av | 国产又粗又猛又色 | 在线黄色av | 日韩在线免费 | 麻豆免费在线视频 | 91丨九色丨91啦蝌蚪老版 | 亚欧日韩av| 亚洲精品网址在线观看 | 国产一区二区高清 | 亚洲高清色综合 | 国产精品21区 | 男女视频国产 | 麻豆国产视频下载 | 国产精品9区 | 国产欧美精品一区aⅴ影院 99视频国产精品免费观看 | 99久视频 | 久久久久免费网 | 中文字幕婷婷 | 午夜视频99 | 亚洲午夜久久久综合37日本 | 黄色毛片一级片 | 国产精品久久久av | 2019免费中文字幕 | av福利在线看 | 五月天中文字幕mv在线 | 久久久久久久久久影视 | 97视频亚洲 | 在线视频 91 | 91丨九色丨国产丨porny精品 | 久久综合九色综合网站 | 久久人人爽人人爽人人片 | 日韩三级视频在线观看 | 国产精品视频在线看 | 精品久久电影 | 国产精品人成电影在线观看 | 黄色小说在线免费观看 | 五月天六月丁香 | 欧美日韩成人 | 欧美色综合久久 | 国产精品久久艹 | 国产在线看 | 2019天天干天天色 | 国产免费又爽又刺激在线观看 | 中文字幕在线观看完整版 | 成人动漫一区二区 | 91黄色在线看 | 91成人天堂久久成人 | 521色香蕉网站在线观看 | 又黄又爽的免费高潮视频 | 丁香婷五月| 日韩专区视频 | 中文字幕一区二区三区视频 | 成人在线观看资源 | 久草视频在线资源 | 一本色道久久综合亚洲二区三区 | 国产亚洲欧美一区 | 久久综合精品一区 | 久久观看免费视频 | 亚洲精品一区二区在线观看 | 免费在线观看日韩视频 | 天天干夜夜擦 | a极黄色片 | 久色 网| 精品一区二区三区久久久 | 国产手机视频在线观看 | 在线看片中文字幕 | 中文字幕资源网在线观看 | 欧美日韩高清不卡 | 成人在线播放网站 | 久久国产乱| 国产精品第 | 成人网页在线免费观看 | 国产麻豆剧传媒免费观看 | 91精品福利在线 | 九色91在线视频 | 精品在线不卡 | 国产精品午夜在线 | 免费看黄电影 | 色婷婷国产 | 中文在线a在线 | 色欧美成人精品a∨在线观看 | 最新的av网站 | 免费观看成人网 | 国产99久久精品 | 午夜视频一区二区 | 久久久久草 | 五月激情站 | 天天射天天干天天插 | 免费看黄的| 国产黄色精品 | 日本中文字幕免费观看 | 天天干夜夜想 | 欧美色伊人| 亚洲精品va | 黄色小说免费在线观看 | 久久免费视频一区 | 国产亚洲字幕 | 精品三级av | 在线黄色观看 | 国产精品亚洲a | 曰韩精品| 在线免费av播放 | 波多野结衣亚洲一区二区 | 国产精品久久久久一区二区国产 | 天天操,夜夜操 | 国产精品白虎 | 国内精品久久久久久 | 亚洲最新av在线网站 | 久久综合九色综合久99 | 国产精品久久99综合免费观看尤物 | 久九视频 | 国产午夜精品一区二区三区欧美 | 在线观看韩国av | 久久综合免费 | 最近中文字幕高清字幕在线视频 | 五月激情丁香 | 狠狠干 狠狠操 | 狠狠久久 | 成人h视频在线播放 | 日韩精品一区二区免费视频 | 最近中文字幕国语免费av | 欧美极度另类性三渗透 | 亚洲影院色| 欧美亚洲成人xxx | 麻豆国产在线播放 | 在线看91| 一二三久久久 | 亚洲japanese制服美女 | 999国内精品永久免费视频 | 91精品一区二区三区蜜臀 | 久草在线网址 | 日韩一级电影在线观看 | 中文字幕国产一区二区 | 伊人久在线 | 中字幕视频在线永久在线观看免费 | 欧美亚洲精品在线观看 | 808电影免费观看三年 | 日本在线观看一区二区 | 奇米网在线观看 | adc在线观看 | 色视频在线看 | 日日草天天草 | www178ccom视频在线 | 久久成人国产精品一区二区 | 亚洲一级黄色大片 | 免费日韩三级 | 亚洲国产中文字幕在线观看 | 五月天激情综合 | 国产精品美女久久久久aⅴ 干干夜夜 | 天天色天天射天天干 | 91中文字幕在线视频 | 黄色一级免费网站 | 久久国产精品久久w女人spa | 欧美一区二区三区在线看 | 久久午夜色播影院免费高清 | 视频1区2区 | 久久人人97超碰精品888 | 国产自偷自拍 | 午夜精品福利影院 | 亚洲精品美女久久久久 | 国产中文字幕在线播放 | 亚洲涩涩网站 | 国产福利av在线 | 天天操天天射天天 | 在线免费精品视频 | 手机在线黄色网址 | 国产精品视频app | 国产成人精品一区二区三区福利 | 婷婷色资源 | 一级黄色网址 | 欧美色伊人 | 日韩精品中文字幕av | 国产精品夜夜夜一区二区三区尤 | 亚洲国产精品人久久电影 | 国产一级二级三级在线观看 | 国产又粗又猛又黄又爽的视频 | 麻豆播放 | 久久tv视频| 精品一区二区免费 | 欧美精品久久久久久久久久 | 亚洲狠狠操| 超碰97国产| 九九热re | 91精品国产欧美一区二区 | 亚洲精品白浆高清久久久久久 | 在线久草视频 | 亚洲精品视频在线免费 | av怡红院| 欧美巨乳波霸 | 久久婷婷亚洲 | 久久精品一二三区白丝高潮 | 日韩久久久久久久久 | 91探花在线 | 成人a级免费视频 | 六月丁香综合网 | 久草在线中文888 | 91成人看片| 婷婷五天天在线视频 | 精品成人网 | 麻豆久久久久久久 | 成人黄色在线观看视频 | 超碰97成人| 麻豆视频在线播放 | 国产手机精品视频 | 在线视频a | 色婷婷一 | 国产中文字幕精品 | 免费影视大全推荐 | 国产一区二区三区高清播放 | 特级毛片aaa | 五月天最新网址 | 免费看黄在线网站 | 国产日韩精品久久 | 久久一精品 | 99精品国产视频 | 在线观看av网站 | 欧美久久久久久久久久久久久 | 免费在线观看av网站 | 欧美午夜剧场 | 色综合在 | 91视频 - v11av| 国产精品久久久一区二区 | 国产成人精品久 | 韩国av不卡 | 黄色特级片 | 一区二区三区播放 | 色久网| 91九色成人蝌蚪首页 | 精品久久久久久久久中文字幕 | 91av视频免费在线观看 | 91精品在线视频观看 | 国产激情久久久 | 在线欧美a | 亚洲成av人电影 | 中文字幕在线观看不卡 | 国产精品国产亚洲精品看不卡 | 久久久精品视频网站 | 手机在线视频福利 | 色婷婷综合成人av | 精品在线观看视频 | 麻豆 91 在线 | 日韩在线观看一区二区 | 国产精品青草综合久久久久99 | 久久草 | www.福利 | 久久99精品久久久久久三级 | 毛片一级免费一级 | 久黄色| 亚洲成aⅴ人片久久青草影院 | 亚洲无在线| 97视频在线观看播放 | 91精品天码美女少妇 | 成人av高清在线观看 | 国产精品国产亚洲精品看不卡 | 一区二区三区福利 | 欧美一级爽| 成 人 免费 黄 色 视频 | 久久成人综合 | 欧美日韩在线播放 | 奇米网网址| 五月婷婷操 | 午夜色婷婷 | 中文字幕第一页在线播放 | www婷婷| 亚洲国产无 | 91成人天堂久久成人 | 五月天婷亚洲天综合网鲁鲁鲁 | 毛片1000部免费看 | 久热色超碰 | 欧美另类z0zx | 国产免费小视频 | 国产视频在线免费观看 | 国产精品成人一区二区三区 | 国产精品短视频 | 99精品亚洲 | 91女人18片女毛片60分钟 | 中文字幕999 | 一区二区三区精品久久久 | 欧美日韩国产精品一区二区 | 91福利国产在线观看 | 最近免费中文字幕mv在线视频3 | 天堂av影院 | 欧美亚洲另类在线视频 | 99热精品在线 | 欧美精品中文字幕亚洲专区 | 午夜影视一区 | 99理论片 | 韩国av免费看 | 在线视频app| 亚洲精品久久久久久久不卡四虎 | 久久精品www人人爽人人 | 91久久久国产精品 | 人人爽人人爽人人爽 | 色资源网在线观看 | 丁香五月亚洲综合在线 | 免费在线国产 | 天天色天天操综合网 | 91视频中文字幕 | 亚洲精品字幕 | 91亚洲精 | 91亚洲精品久久久蜜桃网站 | 超碰在线免费97 | 久久免费激情视频 | 国产小视频免费在线观看 | 国产中文字幕视频在线观看 | 日韩免费观看一区二区三区 | 九九九国产 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 成人免费视频播放 | 欧美激情第八页 | av福利在线播放 | 99国产精品久久久久老师 | 伊人婷婷综合 | 亚洲九九九在线观看 | 精品网站999www | 一区二区三区四区不卡 | 成人网页在线免费观看 | 精品久久久久久一区二区里番 | 少妇精品久久久一区二区免费 | 国产免费一区二区三区网站免费 | 99精品视频在线播放免费 | 99精品欧美一区二区蜜桃免费 | 一区二区视 | 就色干综合 | 特级毛片爽www免费版 | 亚洲久久视频 | 在线观看aaa | 国产特级毛片aaaaaa毛片 | 一区二区三区在线播放 | 天天操夜夜干 | 国产一区在线视频播放 | 在线观看的黄色 | 成人a级免费视频 | 国产91学生粉嫩喷水 | 久操操 | 国产污视频在线观看 | 国产在线看 | 99热99热 | 日韩精品一区电影 | 成人国产精品一区 | 国产中文自拍 | 国产午夜精品一区二区三区 | 五月综合激情 | 中文字幕在线观看免费高清电影 | 丁香六月婷婷激情 | 精品亚洲欧美一区 | 国产高清福利在线 | 婷婷色伊人| 日本福利视频在线 | 69国产成人综合久久精品欧美 | 香蕉一区| 99在线视频网站 | 亚洲欧美日韩国产 | 久久男人免费视频 | 深爱五月激情网 | 国产精品剧情在线亚洲 | 欧洲精品码一区二区三区免费看 | av黄免费看 | 欧美va天堂在线电影 | 在线视频观看成人 | 日韩美女黄色片 | 免费av电影网站 | 国产精品精品久久久久久 | 一区二区三区精品在线视频 | 精品自拍av | 婷婷丁香社区 | 亚洲精品乱码久久久一二三 | 一二三区高清 | 在线观看日韩视频 | 久爱综合| 香蕉视频免费在线播放 | 久久国产亚洲视频 | 国产精品一级视频 | 黄色网址在线播放 | 日韩欧美大片免费观看 | 国产一区二区三区四区大秀 | 成人一区影院 | 免费视频在线观看网站 | 999久久a精品合区久久久 | 91在线日本| 久久免费成人精品视频 | av在线电影免费观看 | 狠狠ri| 日韩在线无 | 久久情爱| 有没有在线观看av | 热久久免费视频精品 | 安徽妇搡bbbb搡bbbb | 欧美成a人片在线观看久 | 亚洲综合小说电影qvod | 在线免费黄色 | 一区在线观看 | 国产精品久久久久久久免费观看 | 九九九九九九精品 | 成年人免费看片 | 日韩羞羞 | 中文区中文字幕免费看 | 欧美一级片在线免费观看 | 韩日电影在线观看 | 麻花豆传媒一二三产区 | 全久久久久久久久久久电影 | 91精品在线免费 | 一区二区视频在线免费观看 | 综合五月婷婷 | 国产一区二区在线免费播放 | 久久不见久久见免费影院 | 色综合 久久精品 | 国产福利在线免费 | 中文字幕国产一区二区 | 精品国产精品久久 | 视频成人永久免费视频 | 天天操夜夜操天天射 | 国产999在线 | 九九免费在线观看 | 欧美日韩免费视频 | 超碰99人人 | 在线免费黄色av | 看片的网址 | 欧美色图亚洲图片 | 一区二区三区四区五区六区 | 婷婷在线综合 | 亚洲黄色三级 | 狠狠操操网| 在线播放av网址 | 欧美性粗大hdvideo | 99在线高清视频在线播放 | 国产视频1区2区3区 久久夜视频 | 丝袜足交在线 | 国产不卡在线视频 | 日本久久不卡视频 | 亚洲国产成人久久 | 欧美少妇18p | 日本中文字幕免费观看 | 日韩在线免费视频 | 色噜噜色噜噜 | 久久久久久久久影视 | 国产午夜激情视频 | 亚洲日韩欧美视频 | 久草在线观看视频免费 | 成人在线一区二区三区 | 午夜精品一区二区三区在线播放 | 亚洲国内精品在线 | 欧美日韩高清免费 | 精品一区二区免费在线观看 | av电影免费观看 | 亚洲午夜精品福利 | 天天av综合网 | 月下香电影 | 免费国产亚洲视频 | 在线电影日韩 | 久久精品这里精品 | 奇米影视777四色米奇影院 | 久久99精品热在线观看 | 五月婷婷视频在线 | 永久免费毛片 | 18国产精品福利片久久婷 | 波多野结衣理论片 | 国产精品成人免费精品自在线观看 | 国产免费又粗又猛又爽 | 久久精品3 | 国产高清中文字幕 | 精品一区精品二区 | 探花在线观看 | 国内精品国产三级国产aⅴ久 | 国产精品免费视频网站 | 成 人 a v天堂 | 丁香六月综合网 | 久久久久久久久久影院 | 亚洲精品欧美精品 | 日本激情视频中文字幕 | 久一久久| 午夜久久福利影院 | 97视频在线看 | 国产精品夜夜夜一区二区三区尤 |