Contents

在 Spring MVC 中使用 Thymeleaf

1.概述

Thymeleaf 是一个 Java 模板引擎,用于处理和创建 HTML、XML、JavaScript、CSS 和文本。

在本教程中,我们将讨论如何在 Spring MVC 应用程序的视图层中使用 Thymeleaf以及一些基本用例。

该库具有极强的可扩展性,其自然的模板功能确保我们可以在没有后端的情况下制作模板原型。与其他流行的模板引擎(例如 JSP)相比,这使得开发速度非常快。

2. 将 Thymeleaf 与 Spring 集成

首先,让我们看看与 Spring 集成所需的配置。集成需要thymeleaf-spring库。 我们将以下依赖项添加到我们的 Maven POM 文件中:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>

请注意,对于 Spring 4 项目,我们必须使用thymeleaf-spring4库而不是thymeleaf-spring5

SpringTemplateEngine类执行所有配置步骤。

我们可以在 Java 配置文件中将这个类配置为 bean:

@Bean
@Description("Thymeleaf Template Resolver")
public ServletContextTemplateResolver templateResolver() {
    ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
    templateResolver.setPrefix("/WEB-INF/views/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML5");
    return templateResolver;
}
@Bean
@Description("Thymeleaf Template Engine")
public SpringTemplateEngine templateEngine() {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver());
    templateEngine.setTemplateEngineMessageSource(messageSource());
    return templateEngine;
}

templateResolver bean 属性prefix suffix 分别指示webapp目录中视图页面的位置及其文件扩展名。

Spring MVC 中的ViewResolver接口将控制器返回的视图名称映射到实际的视图对象。ThymeleafViewResolver实现ViewResolver接口,它用于确定要呈现哪些 Thymeleaf 视图,给定视图名称。

集成的最后一步是将ThymeleafViewResolver添加为 bean:

@Bean
@Description("Thymeleaf View Resolver")
public ThymeleafViewResolver viewResolver() {
    ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
    viewResolver.setTemplateEngine(templateEngine());
    viewResolver.setOrder(1);
    return viewResolver;
}

3.  Spring Boot 中的 Thymeleaf

Spring Boot通过添加spring-boot-starter-thymeleaf 依赖项为Thymeleaf提供自动配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

无需显式配置。默认情况下,HTML 文件应放置在resources/templates位置。

4. 显示来自消息源(属性文件)的值

我们可以使用*th:text=”#{key}”*标签属性来显示属性文件中的值。

为此,我们需要将属性文件配置为messageSource bean:

@Bean
@Description("Spring Message Resolver")
public ResourceBundleMessageSource messageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("messages");
    return messageSource;
}

这是 Thymeleaf HTML 代码,用于显示与键welcome.message关联的值:

<span th:text="#{welcome.message}" />

5. 显示模型属性

5.1. 简单属性

我们可以使用*th:text=”${attributename}”*标签属性来显示模型属性的值。

让我们在控制器类中添加一个名为serverTime的模型属性:

model.addAttribute("serverTime", dateFormat.format(new Date()));

这是显示serverTime属性值的 HTML 代码:

Current time is <span th:text="${serverTime}" />

5.2. 集合属性

如果模型属性是对象的集合,我们可以使用th:each标签属性对其进行迭代。

让我们定义一个包含两个字段id 和nameStudent模型类:

public class Student implements Serializable {
    private Integer id;
    private String name;
    // standard getters and setters
}

现在我们将在控制器类中添加一个学生列表作为模型属性:

List<Student> students = new ArrayList<Student>();
// logic to build student data
model.addAttribute("students", students);

最后,我们可以使用 Thymeleaf 模板代码遍历学生列表并显示所有字段值:

<tbody>
    <tr th:each="student: ${students}">
        <td th:text="${student.id}" />
        <td th:text="${student.name}" />
    </tr>
</tbody>

6.条件评估

6.1. ifunless

如果满足条件,我们使用*th:if=”${condition}”属性来显示视图的一部分。如果条件不满足,我们使用th:unless="${condition}"*属性来显示视图的一部分。

让我们在Student模型中添加一个gender字段:

public class Student implements Serializable {
    private Integer id;
    private String name;
    private Character gender;
    
    // standard getters and setters
}

假设该字段有两个可能的值(M 或 F)来指示学生的性别。 如果我们希望显示单词“Male”或“Female”而不是单个字符,我们可以使用这个 Thymeleaf 代码来做到这一点:

<td>
    <span th:if="${student.gender} == 'M'" th:text="Male" /> 
    <span th:unless="${student.gender} == 'M'" th:text="Female" />
</td>

6.2. switchcase

我们使用th:switchth:case属性来使用 switch 语句结构有条件地显示内容。 让我们使用th:switchth:case属性重写前面的代码:

<td th:switch="${student.gender}">
    <span th:case="'M'" th:text="Male" /> 
    <span th:case="'F'" th:text="Female" />
</td>

7. 处理用户输入

我们可以使用th:action=”@{url}”th:object=”${object}”属性来处理表单输入。我们使用th:action 提供表单操作 URL,使用th:object指定提交的表单数据将绑定到的对象。

使用th:field=”*{name}”属性映射各个字段,其中名称是对象的匹配属性。

对于Student类,我们可以创建一个输入表单:

<form action="#" th:action="@{/saveStudent}" th:object="${student}" method="post">
    <table border="1">
        <tr>
            <td><label th:text="#{msg.id}" /></td>
            <td><input type="number" th:field="*{id}" /></td>
        </tr>
        <tr>
            <td><label th:text="#{msg.name}" /></td>
            <td><input type="text" th:field="*{name}" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Submit" /></td>
        </tr>
    </table>
</form>

在上面的代码中,/saveStudent是表单操作 URL,*student *是保存提交的表单数据的对象。 StudentController类处理表单提交:

@Controller
public class StudentController {
    @RequestMapping(value = "/saveStudent", method = RequestMethod.POST)
    public String saveStudent(@ModelAttribute Student student, BindingResult errors, Model model) {
        // logic to process input data
    }
}

@RequestMapping注解将控制器方法映射到表单中提供的 URL。带注释的方法saveStudent()对提交的表单执行所需的处理。最后,@ModelAttribute注释将表单字段绑定到*student *对象。

8. 显示验证错误

我们可以使用*#fields.hasErrors()函数来检查一个字段是否有任何验证错误。我们使用#fields.errors()*函数来显示特定字段的错误。字段名称是这两个函数的输入参数。

让我们看一下用于迭代并显示表单中每个字段的错误的 HTML 代码:

<ul>
    <li th:each="err : ${#fields.errors('id')}" th:text="${err}" />
    <li th:each="err : ${#fields.errors('name')}" th:text="${err}" />
</ul>

上述函数不接受字段名称,而是接受通配符*或常量all来表示所有字段。我们使用th:each属性来迭代每个字段可能存在的多个错误。 这是之前使用通配符*重写的 HTML 代码:

<ul>
    <li th:each="err : ${#fields.errors('*')}" th:text="${err}" />
</ul>

在这里我们使用常量all

<ul>
    <li th:each="err : ${#fields.errors('all')}" th:text="${err}" />
</ul>

同样,我们可以使用global常量在 Spring 中显示全局错误。 这是显示全局错误的 HTML 代码:

<ul>
    <li th:each="err : ${#fields.errors('global')}" th:text="${err}" />
</ul>

此外,我们可以使用th:errors属性来显示错误消息。

之前在表单中显示错误的代码可以使用th:errors属性重写:

<ul>
    <li th:errors="*{id}" />
    <li th:errors="*{name}" />
</ul>

9. 使用转换

我们使用双括号语法*{{}}来格式化数据以供显示。这利用了为上下文文件的conversionService* bean 中的该类型字段配置的conversionService 。 让我们看看如何格式化Student类中的 name 字段:

<tr th:each="student: ${students}">
    <td th:text="${{student.name}}" />
</tr>

上面的代码使用NameFormatter类,通过覆盖WebMvcConfigurer接口的*addFormatters()*方法进行配置。

为此,我们的*@Configuration类覆盖了WebMvcConfigurerAdapter*类:

@Configuration
public class WebMVCConfig extends WebMvcConfigurerAdapter {
    // ...
    @Override
    @Description("Custom Conversion Service")
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new NameFormatter());
    }
}

NameFormatter类实现了 Spring Formatter接口。

我们还可以使用*#conversions实用程序来转换对象以进行显示。实用程序函数的语法是#conversions.convert(Object, Class),其中Object被转换为Class*类型。

以下是如何在删除小数部分的情况下显示*student 对象percentage *:

<tr th:each="student: ${students}">
    <td th:text="${#conversions.convert(student.percentage, 'Integer')}" />
</tr>