分类
Spring MVC

在Spring中使用Thymeleaf模板引擎

Introduction to Using Thymeleaf in Spring

1. 概述

Thymeleaf是一款优秀的JAVA模板引擎。该引擎工作在服务端,能够处理HTML、XML、JavaScript、css甚至纯文本。Thymeleaf拥有良好的扩展性,与其他流行的模板引擎(例如JSP)相比,拥有更快的开发效率,特别是在团队合作开发中。

本文将阐述如何在Spring MVC应用中的视图层中使用Thymeleaf模板引擎。

2. Thymeleaf与Spring集成

Thymeleaf能够非常轻松的与Spring结合在一起,在Spring MVC项目中,只需要引入依赖spring boot提供的Thymeleaf即可:

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

如果你没有使用Spring Boot,则可参考官方文档进行配置。当然了,那将比使用Spring Boot麻烦的多。

3. 显示多语言文件(Message Source)中的值

th:text=”#{key}”标签可以显示多语言文件中的值。Spring Boot项目默认加载messages多语言系列源。若加载自定义的多语言源,则可以参考以下实现

    /**
     * 手动注册多国语言文件messages
     * Spring Boot其实已经默认注册了messages,所以删除该方法后不会对应用造成影响
     * 在此的代码仅做演示用
     *
     * @return
     */
    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

在Thymeleaf模板中,便可如下使用:

<h1 th:text="#{welcome}"></h1>

则H1中的值将根据当前用户的地区及语言,对应显示不同的内容。比如对简体中文用户显示“您好”,而对英文用户显示"welcome"。

4. 显示模型属性

4.1 简单属性

th:text=”${attributename}”用于显示模型model上的属性。比如我们在模型上添加当前用户currentUser

model.addAttribute("currentUser", "zhangsan");

则在Thymeleaf模板中可以如下显示:

<h2 th:text="${currentUser}"></h2>

4.2 集合Collection属性

如果model上的某个属性类型为集合Collection,则可以使用th:each来达到遍历集合的目的。比如有学生Student类有如下字段:

public class Student {

    private Long id;
    private String name;
    private Boolean sex;
    
    // 省略getter/setter

控制器中绑定如下:

List<Student> students = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    students.add(new Student(Long.valueOf(i), "name" + i, i % 2 == 0));
}
model.addAttribute("students", students);

则在Themeleaf模板中如下进行遍历:

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

5. 条件判断

5.1 if以及unless

th:if=”${condition}”用以当condition成立时进行显示。相反的th:unless=”${condition}” 用以当condition不成立时显示。

比如我们可以如下显示性别:

    <td th:if="${student.sex}" th:text="男"></td>
    <td th:unless="${student.sex}" th:text="女"></td>

5.2 switch以及case

th:switch和th:case的用法当然也不能理解,比如可以使用switch、case如下显示性别:

    <td th:switch="${student.sex}">
        <span th:case="true" th:text="男"></span>
        <span th:case="false" th:text="女"></span>
    </td>

6. 处理输入

Form表单输入可以使用 th:action=”@{url}”以及th:object=”${object}” 轻松完成。th:action用于表示Form表单的提交地址,th:object用以标注Form表单绑定的对象。在表单中直接使用th:field=”*{name}” 来绑定表单中的字段,其中name关键字与object上的属性相对应。比如新建如下添加学生表单:

<form action="#" th:action="@{/student/save}" th:object="${student}" method="post">
    <table border="1">
        <tr>
            <td>ID</td>
            <td><input type="number" th:field="*{id}"/></td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" th:field="*{name}"/></td>
        </tr>
        <tr>
            <td><input type="submit" value="提交"/></td>
        </tr>
    </table>
</form>

上述代码中th:action上的/student/save标明了该表单的提交地址,th:object上的student则是该表单绑定的对象。

然后在C层中可以如下接收:

    @PostMapping("save")
    String save(Student student) {
        // 处理新增学生逻辑
    }

7. 显示校验信息

借助于spring-boot-starter-validation,可以轻松的完成输入数据的校验工作:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

在C层中完成校验仅仅需要加入@Valid注解以及注入BingdingResult:

    @PostMapping("save")
    String save(@Valid Student student, BindingResult errors) {
        if (errors.hasErrors()) {
            return "student/add";
        }

        // 具体持久化学生的代码略过

        return "redirect:/student/success";
    }

在模板中,可以使用#fields.hasErrors()方法来获取是否发生校验错误;使用#fields.hasErrors(final String field), #fields.errors(final String field)方法来更精确到获取到某个字段是否发生校验错误;使用th:errors 属性来显示具体的错误信息:

        <tr>
            <td>ID</td>
            <td><input type="number" th:field="*{id}"/></td>
            <td th:if="${#fields.errors('id')}" th:errors="*{id}">id校验信息</td>
        </tr>
        <tr>
            <td>姓名</td>
            <td><input type="text" th:field="*{name}"/></td>
            <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">name校验信息</td>
        </tr>

此外#fields.errors()还可以接收*all做为参数值,表示:发生的所有的错误的集合。比如实现遍历错误信息并依次输出:

    <h1 th:if="${#fields.hasErrors()}">发生错误</h1>
    <ul>
        <li th:each="err : ${#fields.errors('*')}" th:text="${err}"/>
    </ul>

或者:

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

或者传入global来获取全局的错误信息:

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

8. 数据转换

在Thymeleaf模板中,可以使用{{}}来格式化输出字段。比如我们在输出学生姓名name时,将首字母进行大写,则可以建立实现Formatter接口的如下NameFormatter:

/**
 * 名称首写字大写转换器
 */
public class NameFormatter implements Formatter<String> {

    @Override
    public String parse(String s, Locale locale) throws ParseException {
        if (s != null &amp;&amp; !s.isEmpty()) {
            return s.toUpperCase().charAt(0) + s.substring(1);
        }

        return "";
    }

    @Override
    public String print(String s, Locale locale) {
        return s;
    }
}

并于配置文件中注册该转换器:

@Configuration
public class SpringWebConfig implements WebMvcConfigurer {

    /**
     * 注册转换器
     *
     * @param registry 转换器注册商
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new NameFormatter());
    }

然后便可以在模板中引入{{}}使得Thymeleaf自动调用该转换器了:

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

除按字段名进行转换外,还支持按类型进行转换。比如我们新建如下StudentFormatter用以显示Student类型:

/**
 * 学生实体 转换器
 */
public class StudentFormatter implements Formatter<Student> {

    /**
     * 将字符串转换为Student实体
     * 本例中未使用该方法,直接抛出异常
     *
     * @param text   字符器
     * @param locale 地区
     * @return
     * @throws ParseException
     */
    @Override
    public Student parse(String text, Locale locale) throws ParseException {
        throw new RuntimeException("方法未实现");
    }

    /**
     * 将学生实体转为输出的字符串
     * @param student 学生
     * @param locale 地区
     * @return
     */
    @Override
    public String print(Student student, Locale locale) {
        return student.getId().toString() + "-" + (student.getSex() ? "男" : "女") + "-" + student.getName();
    }
}

注册如下:

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldType(Student.class, new StudentFormatter());
        registry.addFormatter(new NameFormatter());
    }

便可在模板中使用如下代码来显示学生的整体信息:

<td th:text="${{student}}"></td>

最后,还可以使用#conversions 模板中将对象的进行强制转换。#conversions.convert(Object, Class)语法表示将Object转换为Class类型。比如我们将性别sex字段由Boolean类型转换为String类型:

<td th:text="${#conversions.convert(student.sex, 'String')}"></td>

9. 总结

本文对Spring MVC应用集成Thymeleaf模板引擎进行了简单的介绍。Thymeleaf模板引擎可以基于Spring MVC快速的创建应用程序,数据绑定方式灵活、易用。

最后,我们一如既往的为本文提供了完整、可运行的code demo,希望能对你有所帮助。