集成Thymeleaf模板引擎

Thymeleaf是Spring5后比较流行的模板引擎,如果我们开发的工程依然采用后端MVC模式开发,比较推荐使用。

官方网站:https://www.thymeleaf.org/

同类技术对比

在Thymeleaf出现之前,我们通常是用JSP或是Velocity这种模板引擎编写SpringMVC中的V(View),这引起的严重问题就是我们编写的.jsp或是.vm文件和真正的HTML文件格式严重不符,我们想要预览已经编写好的模板,就必须启动后端服务器,比较麻烦。

Thymeleaf模板语法比较像现在很流行的前端框架(比如AngularJS),它通过在HTML中定义一个th命名空间,引入th:系列的XML标签属性,模板的显示逻辑如th:ifth:each都是放在真正的HTML标签中的属性,这样做的好处就是不用启动应用服务器,就能够正确的让浏览器直接解析预览我们的模板,利于程序员和设计人员合作。

当然,Thymeleaf也有缺点,比如语法不如JSP、Velocity等灵活,实际开发任务中经常碰到过于绕弯、难以实现的问题。

引入Maven依赖

SpringMVC中使用Thymeleaf需要如下几个依赖包:

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

注意:引入thymeleaf-spring5这个包是因为这里测试时使用的是SpringMVC5版本,如果使用的是4版本,对应的包是thymeleaf-spring4

在Spring配置文件中配置Thymeleaf的模板处理器

demo-servlet.xml

<!--thymeleaf模板配置-->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
  <property name="templateEngine" ref="templateEngine"/>
  <property name="characterEncoding" value="UTF-8"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
  <property name="templateResolver" ref="templateResolver"/>
</bean>
<bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
  <property name="prefix" value="/template/"/>
  <property name="suffix" value=".html"/>
  <property name="templateMode" value="HTML5" />
  <!--<property name="cacheable" value="false" />-->
  <property name="characterEncoding" value="UTF-8"/>
</bean>

其中templateResolver这个Bean,prefix字段配置了我们模板的存放位置,我们这里模板文件放在src/main/webapp/template文件夹下,suffix字段配置了模板的文件名后缀,cacheable指定模板解析一次后是否缓存,开发阶段可以关闭。

其它配置项都比较简单,这里就不多做解释了。

控制器和模板代码的编写

下面写一个最简单的例子,从控制器中传一个字符串到Thymeleaf模板。

MainController.java

package com.gacfox.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MainController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String getMainPage(Model model) {
        model.addAttribute("msg", "hello, world!!");
        return "main";
    }
}

main.html

<!DOCTYPE html>
<html lang="zh-cmn-Hans" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>测试</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
</body>
</html>

代码比较简单,SpringMVC中@Controller标注的注解,如果控制器方法返回字符串,默认以模板视图的方式处理,返回的字符串就是模板视图名。

Model是一个类似Map的对象,可以用来存储基本类型、字符串、JavaBean等,如果我们的控制器方法声明了该参数,Spring框架会自动注入其实例。我们在Model中保存的数据可以在模板中取用。

Thymeleaf使用简介

Thymeleaf是一个为web设计的现代模板引擎,使用起来比较简便,但是实际上Thymeleaf设计的还是蛮复杂的,这里就简要介绍一些Thymeleaf作为WebMVC程序中的使用方法。

引入XML命名空间

使用Thymeleaf模板前,我们需要先引入XML命名空间。

<html lang="zh" xmlns:th="http://www.thymeleaf.org">

显示控制器传来的变量

基本类型和字符串变量可以直接通过th:text等属性,用${}包裹取用填入模板。

<!--显示字符串-->
<div th:text="${msg}">defaultMsg</div>

显示JavaBean的属性

JavaBean变量可以通过.操作符直接访问其中的对应get方法或public属性。

<!--显示Java Bean-->
<table>
    <tr>
        <th>用户名</th>
        <th>密码</th>
        <th>出生日期</th>
    </tr>
    <tr>
        <td th:text="${user.username}">defaultUsername</td>
        <td th:text="${user.password}">defaultPassword</td>
        <td th:text="${user.birthday}">defaultBirthday</td>
    </tr>
</table>

迭代显示List数据

th:each用于迭代显示数据,下面是两个例子:

<!--迭代显示List-->
<ul>
    <li th:each="s : ${strings}" th:text="${s}">defaultItem</li>
</ul>
<!--迭代显示Java Bean的List-->
<table>
    <tr>
        <th>用户名</th>
        <th>密码</th>
        <th>出生日期</th>
    </tr>
    <tr th:each="u : ${userList}">
        <td th:text="${u.username}"></td>
        <td th:text="${u.password}"></td>
        <td th:text="${u.birthday}"></td>
    </tr>
</table>

条件判断

th:if用于判断,Thymeleaf中没有完整的if-else,比较推荐的用法是写多个if逻辑。

<!--判断-->
<span th:if="${var} == 1">aa</span>
<span th:if="${var} == 2">bb</span>

通过session进行登录判断

模板中,我们可以直接用session对象访问Servlet规范中的Session数据。不过如果我们的Session是保存在Redis中自己维护的,则还需要手动传入。

<!--登录判断-->
<span th:if="!${session.user}">请登录</span>
<span th:if="${session.user}">已登录</span>
</body>
</html>

使用工具类

Thymeleaf中提供了一系列工具类,我们可以使用#符号引用他们,下面例子我们引用日期工具类#dates,实现格式化输出Date对象:

<td th:text="${#dates.format(u.birthday, 'yyyy-MM-dd')}"></td>

使用URL

Thymeleaf中可以自动处理URL,不需要我们在前端代码中判断Context Path等信息,下面是一个例子:

<!--URL-->
<a th:href="@{/test2}">URL</a>

注:最开始的/代表项目的Context Path。

绑定表单字段并使用javax.validation进行后端验证

对于表单操作,Thymeleaf做了一系列封装。下面例子中写了一个简单的登录表单的例子,例子中使用javax.validation注解API进行的后端的表单字段验证,并演示了如何将FormBean绑定到HTML的<form>控件上,以及如何将注解式验证的错误信息输出到Thymeleaf模板上。

LoginFormBean.java

package com.gacfox.demo.bean;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class LoginFormBean {
    @NotNull(message = "用户名能为空")
    @Size(min = 1, max = 255, message = "用户名必须在1-255个字符之间")
    @Pattern(regexp = "[a-zA-Z0-9]+", message = "用户名必须是字母和数字")
    private String username;
    @NotNull(message = "密码不能为空")
    @Size(min = 1, max = 20, message = "密码必须在1-20个字符之间")
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "LoginFormBean{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

MainController.java

package com.gacfox.demo.controller;

import com.gacfox.demo.bean.LoginFormBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;

@Controller
public class MainController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String getTest2Page(Model model) {
        model.addAttribute("loginFormBean", new LoginFormBean());
        return "main";
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String doPost(@Valid LoginFormBean loginFormBean, BindingResult bindingResult, Model model) {
        if (!bindingResult.hasErrors()) {
            System.out.println(loginFormBean);
            if ("root".equals(loginFormBean.getUsername()) && "123".equals(loginFormBean.getPassword())) {
                return "redirect:/";
            } else {
                model.addAttribute("err_msg", "用户名或密码错误");
                return "main";
            }
        } else {
            return "main";
        }
    }
}

main.html

<!DOCTYPE html>
<html lang="zh-cmn-Hans" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>测试</title>
</head>
<body>
<form th:action="@{/}" method="post" th:object="${loginFormBean}">
    <input type="text" name="username" th:value="${loginFormBean.username}">
    <input type="password" name="password" th:value="${loginFormBean.password}">
    <input type="submit" value="提交">
    <span th:if="${err_msg != null}" th:text="${err_msg}"></span>
    <span th:errors="${loginFormBean.username}"></span>
    <span th:errors="${loginFormBean.password}"></span>
</form>
</body>
</html>

@Valid表单验证的原理其它章节已经介绍过了,这里我们主要关注模板的编写:

  • th:action:值是一个URL语法,用于指定表单的提交地址,使用@{}而不是相对路径的好处是可以自动处理Context Path。
  • th:object:用于将FormBean绑定到HTML的form标签,FormBean需要我们首先实例化并通过控制器传入模板中。
  • th:errors:用户回显错误信息。
  • th:value:用于填充表单字段值。在上面例子中,这个标签用于在提交失败时回显上次的表单的数据,以免用户全部重填。
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap