Thymeleaf 3.1:有什么新功能以及如何迁移
最新版本是 Thymeleaf3.1.3.RELEASE
.
有什么新功能
支持 Servlet API 5.0 以及jakarta.*
类命名空间
Thymeleaf 3.1 增加了对自版本 5.0 以来在 Servlet API 中引入的新jakarta.*
类命名空间的支持,同时并未移除对之前版本中javax.*
类的支持。
对 Spring 6.0 的支持
Thymeleaf 3.1 添加了一个新的thymeleaf-spring6
核心库以集成 Spring Framework 6.0。
对早于 Spring 5.0 的 Spring 版本的支持已被移除。
对 Spring Security 6.0 的支持
Thymeleaf 3.1 添加了一个新的thymeleaf-extras-springsecurity6
核心库以集成 Spring Security 6.0。
对早于 Spring Security 5.0 的 Spring Security 版本的支持已被移除。
对java.time
extras 模块的核心支持已整合进 Thymeleaf 核心:
这个thymeleaf-extras-java8time
表达式工具对象现在始终可用。#temporals
Java 兼容性
现在最低通常需要的 JDK 版本为 JDK 8。
现在 JDK 8 是最低通常要求的版本。
核心库所需的最低 JDK 版本为 JDK 17。thymeleaf-spring6
和thymeleaf-extras-springsecurity6
核心库所需的最低版本为 JDK 17。
移除了基于 Web API 的表达式工具对象
这个#request
, #response
, #session
访问,并且#servletContext
在 Thymeleaf 3.1 的表达式中不再可用。
对表达式中类使用更严格的限制
Thymeleaf 3.1 建立了一般性的限制,禁止使用核心包中的类:java.*
, javax.*
, jakarta.*
, jdk.*
, org.ietf.jgss.*
, org.omg.*
, org.w3c.dom.*
, org.xml.sax.*
, com.sun.*
和sun.*
.
这些包中的类的方法/构造函数调用现在被禁止,静态引用也是如此。
作为此限制的例外,这些包中的某些类始终被允许:
基础
java.lang.*
和java.math.*
类:java.lang.Boolean
,java.lang.Byte
,java.lang.Character
,java.lang.Double
,java.lang.Enum
,java.lang.Float
,java.lang.Integer
,java.lang.Long
,java.lang.Math
,java.lang.Number
,java.lang.Short
,java.lang.String
,java.math.BigDecimal
,java.math.BigInteger
,java.math.RoundingMode
.集合类和接口:
java.util.Collection
,java.util.Enumeration
,java.util.Iterable
,java.util.Iterator
,java.util.List
,java.util.ArrayList
,java.util.LinkedList
,java.util.Set
,java.util.HashSet
,java.util.LinkedHashSet
,java.util.Map
,java.util.Map.Entry
,java.util.HashMap
,java.util.LinkedHashMap
注意:接口方法(例如Map#get(key)
)通常允许任何实现,但此处列出的具体实现还可以被构造并进行静态引用。在其他常用类中
java.util.*
:java.util.Properties
,java.util.Optional
,java.util.stream.Stream
,java.util.Locale
,java.util.Date
,java.util.Calendar
.
某些构件已被弃用,并移除了先前已经被弃用的构件
在 Thymeleaf 3.1 中,一些构件已被弃用。
- 已弃用
th:include
推荐使用th:insert
。请注意th:insert
的语义与th:include
. - 有些不同
~{template :: fragment}
should always be used instead of simplytemplate :: fragment
.
Also, artifacts previously deprecated in 3.0 have been removed:
- Removed
th:substituteby
, deprecated previously in favour ofth:replace
. - Removed deprecated use of
execInfo
as a context variable (${execInfo}
), available since 3.0 as an expression utility object (${#execInfo}
)。
Other minor improvements
- General update of dependency versions.
- Allow
#temporals
expression utility object to format temporals in non-default locales. - Support iterating (e.g.
th:each
) directly on java streams (java.util.stream.Stream
)。 - Allow
SpringTemplateEngine
instances to be configured a custom (even non-Spring) message resolver.
(对于开发者)项目源代码仓库结构的全面改版
Thymeleaf 3.1 包括对(以前多个)源代码仓库的全面改版,并从开发的角度大大改进了示例应用程序的处理方式:
- 将大部分之前的 Thymeleaf 代码仓库整合到
thymeleaf
GitHub 仓库中,该仓库现在包含:- 新的 Thymeleaf BOM (
thymeleaf-parent
) 整合并统一了所有 thymeleaf 依赖项和构建配置。 - 所有 Thymeleaf 核心库,包括与 Spring 和 Spring Security 的集成。
- 所有 Thymeleaf 测试库及其集成。
- 所有 Thymeleaf 测试仓库。
- 所有 Thymeleaf 官方示例应用程序,包括核心、Spring、Spring Security 和 Spring Boot 基础的示例应用。
- 新的 Thymeleaf BOM (
- 创建一个大型 Maven 多模块配置,用于构建完整的 Thymeleaf 模块树。
- 配置示例应用程序,以便可以从 Maven 命令行执行非基于 Spring Boot 的 Web 应用程序。
- 创建更完整的分发包
.zip
形式,现在不仅包括库文件,还包括二进制和(可构建的)源代码形式的示例应用程序。 - 将所有测试基础设施迁移到 JUnit 5。
迁移到 Thymeleaf 3.1
JDK 版本
Thymeleaf 3.1 将其最低兼容级别提升到了 JDK 8,但是thymeleaf-spring6
和thymeleaf-extras-springsecurity6
需要 JDK 17,因为这是 Spring 6.0 所需的 JDK 版本。
Spring 6.0和Spring Security 6.0(以及Spring Boot 3.0)
Thymeleaf与Spring 6.0及Spring Security 6.0的新集成配置方式与之前Spring 5.x中的方式相同(并且仍然适用)。
除了将之前的thymeleaf-spring5
或thymeleaf-extras-springsecurity5
依赖项替换为新的thymeleaf-spring6
或thymeleaf-extras-springsecurity6
属性标记。
之外,不需要进行其他更改。如果是基于Spring Boot的应用程序,则无需任何更改。当添加Thymeleaf Spring Boot启动器时,新的Spring Boot 3.0已经配置并使用Thymeleaf 3.1。
表达式限制
为了提高模板的安全性,Thymeleaf 3.1对变量表达式(${...}
和*{...}
)采取了一系列限制措施,这可能会对你现有的代码产生影响。
如“新增功能”部分所解释的那样,表达式工具对象#request
, #response
, #session
和#servletContext
在模板中的表达式中将不再可用。
推荐的替代方法是,在控制器级别上将模板所需的信息以模型属性的形式添加到模型中。可以通过model#addAttribute(...)
控制器代码中显式添加,或者通过@ModelAttribute
甚至@ControllerAdvice
注解方式完成。
@ModelAttribute("contextPath")
public String contextPath(final HttpServletRequest request) {
return request.getContextPath();
}
另外,还如“新增功能”部分详细说明的那样,对于属于JDK和Jakarta EE核心类的一些类建立了严格的使用限制,部分类除外。从Thymeleaf 3.1开始,变量表达式中将不能使用禁用类的对象。
如果你的某些模板确实需要在禁用类的对象上执行表达式,你可以创建一个包装类(位于你自己的应用程序包中),该类将其方法委托给原始对象,这样就可以在变量表达式中使用它了。
th:include 的弃用
如果你的模板使用了th:include
属性,请注意这在Thymeleaf 3.1中仍然允许,但将在库的未来版本中移除。强烈建议你尽快将所有th:include
元素th:insert
替换为th:insert或th:replace,但要注意它们的工作方式并不完全相同。
因为th:include
th:insert仅插入片段的内容,使得以下代码:生成这样的结果:
<div id="main" th:include="~{::frag}">...</div>
...
<p th:fragment="frag" class="content">
something
</p>
……结果变成这样:
<div id="main">
something
</div>
而使用th:insert
th:replace则会插入整个片段,包括定义它的标签,所以这样写:
<div id="main" th:insert="~{::frag}">...</div>
...
<p th:fragment="frag" class="content">
something
</p>
……结果变成这样:
<div id="main">
<p class="content">
something
</p>
</div>
如果你需要特别获得与th:include
相同的结果,你需要结合使用th:insert
和th:remove
,类似如下方式:
<div id="main" th:insert="~{::frag}">...</div>
...
<p th:fragment="frag" th:remove="tag" class="content">
something
</p>
……这样就会得到以下结果:
<div id="main">
something
</div>
同时请记住,也可以使用<th:block>
标签来定义片段,该标签在求值后总是会被隐藏,从而提供更高的灵活性:
<div id="main" th:insert="~{::frag}">...</div>
...
<th:block th:fragment="frag">
something
</th:block>
结果将是:
<div id="main">
something
</div>
不包裹的片段表达式的弃用
Thymeleaf中的片段表达式表示为~{...}
,它们可以用在许多类型的属性和表达式中,尽管通常出现在th:insert
和th:replace
属性中填充值。
值中。th:insert
或th:replace
在Thymeleaf 3.1之前,诸如th:include
(或已弃用的~{...}
)这样的属性允许指定不带:
<div id="top" th:insert="common :: header">...</div>
包裹语法的片段表达式。但从Thymeleaf 3.1开始,这种语法虽然仍能工作,但被视为已弃用,并计划在未来的版本中删除。现在应像下面这样书写:
<div id="top" th:insert="~{common :: header}">...</div>