Introduction to Using Thymeleaf in Spring – Spring使用百里香的介绍

最后修改: 2016年 1月 9日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Thymeleaf is a Java template engine for processing and creating HTML, XML, JavaScript, CSS and text.

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

In this tutorial, we will discuss how to use Thymeleaf with Spring along with some basic use cases in the view layer of a Spring MVC application.

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

The library is extremely extensible, and its natural templating capability ensures we can prototype templates without a back end. This makes development very fast when compared with other popular template engines such as JSP.

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

2. Integrating Thymeleaf With Spring

2.将百里香与Spring结合起来

First, let’s see the configurations required to integrate with Spring. The thymeleaf-spring library is required for the integration.

首先,让我们看看与Spring集成所需的配置。thymeleaf-spring库是整合所需的。

We’ll add the following dependencies to our Maven POM file:

我们将在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>

Note that, for a Spring 4 project, we have to use the thymeleaf-spring4 library instead of thymeleaf-spring5.

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

The SpringTemplateEngine class performs all of the configuration steps.

SpringTemplateEngine类执行了所有的配置步骤。

We can configure this class as a bean in the Java configuration file:

我们可以在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;
}

The templateResolver bean properties prefix and suffix indicate the location of the view pages within the webapp directory and their filename extension, respectively.

templateResolverbean属性prefixsuffix分别表示视图页面在webapp目录中的位置和它们的文件名扩展名。

The ViewResolver interface in Spring MVC maps the view names returned by a controller to actual view objects. ThymeleafViewResolver implements the ViewResolver interface, and it’s used to determine which Thymeleaf views to render, given a view name.

Spring MVC中的ViewResolver接口将控制器返回的视图名称映射为实际的视图对象。ThymeleafViewResolver实现了ViewResolver接口,它被用来确定在给定的视图名称下渲染哪些Thymeleaf视图。

The final step in the integration is to add the ThymeleafViewResolver as a bean:

集成的最后一步是将ThymeleafViewResolver作为一个bean添加。

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

3. Thymeleaf in Spring Boot

3.Spring Boot中的Thymeleaf

Spring Boot provides auto-configuration for Thymeleaf by adding the spring-boot-starter-thymeleaf dependency:

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>

No explicit configuration is necessary. By default, HTML files should be placed in the resources/templates location.

没有必要进行明确的配置。默认情况下,HTML文件应该放在resources/templates位置。

4. Displaying Values From Message Source (Property Files)

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

We can use the th:text=”#{key}” tag attribute to display values from property files.

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

For this to work, we need to configure the property file as a messageSource bean:

为了使其发挥作用,我们需要将属性文件配置为messageSource bean。

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

Here is the Thymeleaf HTML code to display the value associated with the key welcome.message:

下面是Thymeleaf的HTML代码,显示与键welcome.message相关的值。

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

5. Displaying Model Attributes

5.显示模型属性

5.1. Simple Attributes

5.1.简单属性

We can use the th:text=”${attributename}” tag attribute to display the value of model attributes.

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

Let’s add a model attribute with the name serverTime in the controller class:

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

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

And here’s the HTML code to display the value of serverTime attribute:

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

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

5.2. Collection Attributes

5.2.收集属性

If the model attribute is a collection of objects, we can use the th:each tag attribute to iterate over it.

如果模型属性是一个对象的集合,我们可以使用th:each标签属性来遍历它。

Let’s define a Student model class with two fields, id and name:

让我们定义一个Student模型类,有两个字段:idname

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

Now we will add a list of students as model attribute in the controller class:

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

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

Finally, we can use Thymeleaf template code to iterate over the list of students and display all field values:

最后,我们可以使用Thymeleaf模板代码来迭代学生列表并显示所有字段的值。

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

6. Conditional Evaluation

6.条件性评价

6.1. if and unless

6.1.如果没有

We use the th:if=”${condition}” attribute to display a section of the view if the condition is met. And we use the th:unless=”${condition}” attribute to display a section of the view if the condition is not met.

我们使用th:if=”${condition}”属性,在条件满足的情况下显示视图的某个部分。我们使用th:unless=”${condition}”属性,在条件未满足时显示视图的一部分。

Let’s add a gender field to the Student model:

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

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

Suppose this field has two possible values (M or F) to indicate the student’s gender.

假设这个字段有两个可能的值(M或F)来表示学生的性别。

If we wish to display the words “Male” or “Female” instead of the single character, we could do this using this Thymeleaf code:

如果我们希望显示 “男 “或 “女 “的字样,而不是单一的字符,我们可以用这个Thymeleaf代码来实现。

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

6.2. switch and case

6.2.开关箱子

We use the th:switch and th:case attributes to display content conditionally using the switch statement structure.

我们使用th:switchth:case属性,使用开关语句结构有条件地显示内容。

Let’s rewrite the previous code using the th:switch and th:case attributes:

让我们使用th:switchth:case属性重写之前的代码。

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

7. Handling User Input

7.处理用户的输入

We can handle form input using the th:action=”@{url}” and th:object=”${object}” attributes. We use th:action to provide the form action URL and th:object to specify an object to which the submitted form data will be bound.

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

Individual fields are mapped using the th:field=”*{name}” attribute, where the name is the matching property of the object.

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

For the Student class, we can create an input form:

对于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>

In the above code, /saveStudent is the form action URL and a student is the object that holds the form data submitted.

在上面的代码中,/saveStudent是表单动作的URL,student是持有提交的表单数据的对象。

The StudentController class handles the form submission:

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
    }
}

The @RequestMapping annotation maps the controller method with the URL provided in the form. The annotated method saveStudent() performs the required processing for the submitted form. Finally, the @ModelAttribute annotation binds the form fields to the student object.

@RequestMapping注解将控制器方法与表单中提供的URL进行映射。注解的方法saveStudent()对提交的表单进行必要的处理。最后,@ModelAttribute 注解将表单字段与student对象绑定。

8. Displaying Validation Errors

8.显示验证错误

We can use the #fields.hasErrors() function to check if a field has any validation errors. And we use the #fields.errors() function to display errors for a particular field. The field name is the input parameter for both these functions.

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

Let’s take a look at the HTML code to iterate and display the errors for each of the fields in the form:

让我们看一下HTML代码,以迭代并显示表单中每个字段的错误。

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

Instead of field name, the above functions accept the wild card character * or the constant all to indicate all fields. We used the th:each attribute to iterate the multiple errors that may be present for each of the fields.

上述函数接受通配符*或常数all来代替字段名,以表示所有字段。我们使用th:each属性来遍历每个字段可能存在的多个错误。

Here’s the previous HTML code rewritten using the wildcard *:

下面是使用通配符*重写的先前的HTML代码。

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

And here we’re using the constant all:

而这里我们使用的是常数all

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

Similarly, we can display global errors in Spring using the global constant.

同样地,我们可以使用global常量在Spring中显示全局错误。

Here’s the HTML code to display global errors:

下面是显示全局错误的HTML代码。

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

Also, we can use the th:errors attribute to display error messages.

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

The previous code to display errors in the form can be rewritten using th:errors attribute:

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

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

9. Using Conversions

9.使用转换法

We use the double bracket syntax {{}} to format data for display. This makes use of the formatters configured for that type of field in the conversionService bean of the context file.

我们使用双括号语法{{}}来格式化数据以便显示。这利用了在上下文文件的conversionServiceBean中为该类型字段配置的formatters

Let’s see how to format the name field in the Student class:

让我们看看如何格式化Student类中的姓名字段。

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

The above code uses the NameFormatter class, configured by overriding the addFormatters() method from the WebMvcConfigurer interface.

上述代码使用NameFormatter类,通过覆盖addFormatters()方法从WebMvcConfigurer接口配置。

For this purpose, our @Configuration class overrides the WebMvcConfigurerAdapter class:

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

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

The NameFormatter class implements the Spring Formatter interface.

NameFormatter类实现了Spring Formatter接口。

We can also use the #conversions utility to convert objects for display. The syntax for the utility function is #conversions.convert(Object, Class) where Object is converted to Class type.

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

Here’s how to display student object percentage field with the fractional part removed:

下面是如何显示student对象percentage字段,去掉小数部分。

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

10. Conclusion

10.结论

In this article, we’ve seen how to integrate and use Thymeleaf in a Spring MVC application.

在这篇文章中,我们已经看到了如何在Spring MVC应用中集成和使用Thymeleaf。

We have also seen examples of how to display fields, accept input, display validation errors, and convert data for display.

我们还看到了如何显示字段、接受输入、显示验证错误和转换数据显示的例子。

A working version of the code shown in this article is available in the GitHub repository.

本文所示代码的工作版本可在GitHub 仓库中找到。