整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

SpringBoot系列(六)thymeleaf完整详细版

  1. thymeleaf简介
  2. thymeleaf特点
  3. thymeleaf在SpringBoot的应用
  4. SpringBoot引入Thymeleaf
  5. controller配置
  6. thymeleaf页面测试编写
  7. 结果展示
  8. 其他thymeleaf语法


1. thymeleaf简介

 1. Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎。

 2. Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 -HTML可以在浏览器中正确显示,也可以作为静态原型工作,从而可以在开发团队中加强协作。

 3. Thymeleaf拥有适用于Spring Framework的模块,与您喜欢的工具的大量集成以及插入您自己的功能的能力,对于现代HTML5 JVM Web开发而言,Thymeleaf是理想的选择-尽管它还有很多工作要做。

2. thymeleaf特点

 1. thymeleaf在有网络无网络的环境下都可以运行,所以可以直接在浏览器打开查看静态页面效果。它支持HTML原型,可以在HTML标签里面添加其他属性来实现数据渲染。

 2. thymeleaf具有开箱即用的特性,Thymeleaf是Spring boot推荐使用的模版引擎,直接以html显示,前后端可以很好的分离。

3. thymeleaf在SpringBoot的应用

 1. 国际化,渲染不同国家的语言

 2. 共同页面显示,比如统一异常页面处理,共同的页面处理

4. SpringBoot引入Thymeleaf

 新建一个Springboot web项目,然后添加以下依赖。

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

 然后在配置文件里面添加如下依赖。

spring:
  thymeleaf:
    cache: false 
    prefix: classpath:/templates/
    encoding: UTF-8 #编码
    suffix: .html #模板后缀
    mode: HTML #模板

配置说明:

cache这一行是将页面的缓存关闭,不然我们改变页面之后可能不能及时看到更改的内容,默认是true。

prefix是配置thymeleaf模板所在的位置。

encoding 是配置thymeleaf文档的编码,后面的就不说了

5. controller配置

 上面我们配置好了环境之后就可以创建一个controller文件夹,然后写一个controller,来测试我们的thymeleaf是否成功引入。顺便创建一个对象。 代码:

@Controller
public class ThymeleafController {

    @GetMapping("/getStudents")
    public ModelAndView getStudent(){
        List<Student> students = new LinkedList<>();
        Student student = new Student();
        student.setId(1);
        student.setName("全栈学习笔记");
        student.setAge(21);
        Student student1 = new Student();
        student1.setId(2);
        student1.setName("张三");
        student1.setAge(22);
        students.add(student);
        students.add(student1);
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("students",students);
        modelAndView.setViewName("students");
        return modelAndView;
    }
}

代码解释 :我们创建一个list,然后在list里面添加数据,一遍一次将数据传到页面使用。然后我们创建一个ModelAndView的对象,将list放入这个modeAndView对象中,第一个参数是需要放到model中的属性名称相当于是一个键,第二个是,是一个对象。然后利用setViewName方法,设置要跳转的页面或者说是将数据传到对应的页面

 最外层我们使用了一个 @Controller,这个注解是用来返回一个页面或者视图层的。

 当然,返回ModelAndView对象只是一种方法,还有其他的方法,比如说下面这样

@RequestMapping("/getString")
public String getString(HttpServletRequest request){
    String name = "全栈学习笔记";
    request.setAttribute("name",name);
    return "index.html";
}

利用http的request传值。 然后还有这样

@RequestMapping("/getModel")
public String getModel(Model model){
    model.addAttribute("key","这是一个键");
    return "index.html";
}

 去掉末尾的.html也可以,因为我们在配置文件里面设置了文件的格式为HTML文件。return的字符串都是对应的HTML文件的名称。

实体类代码如下:

/**
 * (Student)实体类
 *
 * @author 全栈学习笔记
 * @since 2020-04-14 11:39:10
 */
public class Student  {
    private static final long serialVersionUID = -91969758749726312L;
    /**
    * 唯一标识id
    */
    private Integer id;
    /**
    * 姓名
    */
    private String name;
    /**
    * 年龄
    */
    private Integer age;
    //省略get,set方法,自己加上
}

6. 页面编写

 写好代码就等页面了,在templates文件夹下面创建一个students.html文件,编写如下代码

<!DOCTYPE html>
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table border="1">
    <tr>
        <td>ID</td>
        <td>姓名</td>
        <td>年龄</td>
    </tr>
    <tr th:each="student:${students}">
        <td th:text="${student.id}"></td>
        <td th:text="${student.name}"></td>
        <td th:text="${student.age}"></td>
    </tr>
</table>
</body>
</html>

 这里有一个很重要的事情就是,我们使用thymeleaf模板之前必须先引入thymeleaf,如下。

<html lang="en" xmlns:th="https://www.thymeleaf.org/">

 这个很关键,不然你就用不了这个thymeleaf语法规则。

代码说明:你可以看到th:each 这个语法,是用来遍历的,类似于for循环,然后我们通过th:text 这个语法来渲染文字。

7.测试结果显示

 运行项目,浏览器输入localhost:8089/getStudents


8.thymeleaf常用的语法

常用的语法:

<!-- 对象 -->
<div th:object="${student}">
    <p th:text="id"></p>
    <p th:text="name"></p>
    <p th:text="age"></p>
</div>
<!-- 逻辑判断 -->
th:if
th:else
<!-- 分支控制 -->
th:switch
th:case

<!--循环 -->
th:each
<!-- 运算 -->
<p th:text="${age}%2 == 0"></p>
<!-- 赋制value -->
th:value
<!-- 链接 -->
th:href

本期讲解就到这里,如果你觉得本文对你有用,可以点个赞,点个关注哦!下一期更精彩!wx search 全栈学习笔记!点个关注不迷路。

文地址:https://dwz.cn/2UR4feq8

作者:yizhiwazi

学习目标

  • 快速掌握Thymeleaf的基本使用(五大基础语法+常用内置对象)

使用教程

温馨提示:Thymeleaf 最为显著的特征是增强属性,任何属性都可以通过th:xx 来完成交互,例如th:value最终会覆盖value属性。

一、基础语法

变量表达式 ${}

使用方法:直接使用th:xx = "${}" 获取对象属性 。例如:

<form id="userForm">
 <input id="id" name="id" th:value="${user.id}"/>
 <input id="username" name="username" th:value="${user.username}"/>
 <input id="password" name="password" th:value="${user.password}"/>
</form>
<div th:text="hello"></div>
<div th:text="${user.username}"></div>

选择变量表达式 *{}

使用方法:首先通过th:object 获取对象,然后使用th:xx = "*{}"获取对象属性。

这种简写风格极为清爽,推荐大家在实际项目中使用。 例如:

<form id="userForm" th:object="${user}">
 <input id="id" name="id" th:value="*{id}"/>
 <input id="username" name="username" th:value="*{username}"/>
 <input id="password" name="password" th:value="*{password}"/>
</form>

链接表达式 @{}

使用方法:通过链接表达式@{}直接拿到应用路径,然后拼接静态资源路径。例如:

<script th:src="@{/webjars/jquery/jquery.js}"></script>
<link th:href="@{/webjars/bootstrap/css/bootstrap.css}" rel="stylesheet" type="text/css">

片段表达式 ~{}

片段表达式是Thymeleaf的特色之一,细粒度可以达到标签级别,这是JSP无法做到的。

片段表达式拥有三种语法:

  • ~{ viewName } 表示引入完整页面
  • ~{ viewName ::selector} 表示在指定页面寻找片段 其中selector可为片段名、jquery选择器等
  • ~{ ::selector} 表示在当前页寻找

使用方法:首先通过th:fragment定制片段 ,然后通过th:replace 填写片段路径和片段名。例如:

<!-- /views/common/head.html-->
<head th:fragment="static">
 <script th:src="@{/webjars/jquery/3.3.1/jquery.js}"></script>
</head>
<!-- /views/your.html -->
<div th:replace="~{common/head::static}"></div>

在实际使用中,我们往往使用更简洁的表达,去掉表达式外壳直接填写片段名。例如:

<!-- your.html -->
<div th:replace="common/head::static"></div>

值得注意的是,使用替换路径th:replace 开头请勿添加斜杠,避免部署运行的时候出现路径报错。(因为默认拼接的路径为spring.thymeleaf.prefix = classpath:/templates/)

消息表达式

即通常的国际化属性:#{msg} 用于获取国际化语言翻译值。例如:

 <title th:text="#{user.title}"></title>

其它表达式

在基础语法中,默认支持字符串连接、数学运算、布尔逻辑和三目运算等。例如:

<input name="name" th:value="${'I am '+(user.name!=null?user.name:'NoBody')}"/>

二、内置对象

官方文档: 附录A: Thymeleaf 3.0 基础对象

官方文档: 附录B: Thymeleaf 3.0 工具类

七大基础对象:

  • ${#ctx} 上下文对象,可用于获取其它内置对象。
  • ${#vars}: 上下文变量。
  • ${#locale}:上下文区域设置。
  • ${#request}: HttpServletRequest对象。
  • ${#response}: HttpServletResponse对象。
  • ${#session}: HttpSession对象。
  • ${#servletContext}: ServletContext对象。

常用的工具类:

  • #strings:字符串工具类
  • #lists:List 工具类
  • #arrays:数组工具类
  • #sets:Set 工具类
  • #maps:常用Map方法。
  • #objects:一般对象类,通常用来判断非空
  • #bools:常用的布尔方法。
  • #execInfo:获取页面模板的处理信息。
  • #messages:在变量表达式中获取外部消息的方法,与使用#{...}语法获取的方法相同。
  • #uris:转义部分URL / URI的方法。
  • #conversions:用于执行已配置的转换服务的方法。
  • #dates:时间操作和时间格式化等。
  • #calendars:用于更复杂时间的格式化。
  • #numbers:格式化数字对象的方法。
  • #aggregates:在数组或集合上创建聚合的方法。
  • #ids:处理可能重复的id属性的方法。

三、迭代循环

想要遍历List集合很简单,配合th:each 即可快速完成迭代。例如遍历用户列表:

<div th:each="user:${userList}">
 账号:<input th:value="${user.username}"/>
 密码:<input th:value="${user.password}"/>
</div>

在集合的迭代过程还可以获取状态变量,只需在变量后面指定状态变量名即可,状态变量可用于获取集合的下标/序号、总数、是否为单数/偶数行、是否为第一个/最后一个。例如:

<div th:each="user,stat:${userList}" th:class="${stat.even}?'even':'odd'">
 下标:<input th:value="${stat.index}"/>
 序号:<input th:value="${stat.count}"/>
 账号:<input th:value="${user.username}"/>
 密码:<input th:value="${user.password}"/>
</div>

如果缺省状态变量名,则迭代器会默认帮我们生成以变量名开头的状态变量 xxStat, 例如:

<div th:each="user:${userList}" th:class="${userStat.even}?'even':'odd'">
 下标:<input th:value="${userStat.index}"/>
 序号:<input th:value="${userStat.count}"/>
 账号:<input th:value="${user.username}"/>
 密码:<input th:value="${user.password}"/>
</div>

四、条件判断

条件判断通常用于动态页面的初始化,例如:

<div th:if="${userList}">
 <div>的确存在..</div>
</div>

如果想取反则使用unless 例如:

<div th:unless="${userList}">
 <div>不存在..</div>
</div>

五、日期格式化

使用默认的日期格式(toString方法) 并不是我们预期的格式:Mon Dec 03 23:16:50 CST 2018

<input type="text" th:value="${user.createTime}"/>

此时可以通过时间工具类#dates来对日期进行格式化:2018-12-03 23:16:50

<input type="text" th:value="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}"/>

六、内联写法

  • (1)为什么要使用内联写法?·答:因为 JS无法获取服务端返回的变量。
  • (2)如何使用内联表达式?答:标准格式为:[[${xx}]] ,可以读取服务端变量,也可以调用内置对象的方法。例如获取用户变量和应用路径:
 <script th:inline="javascript">
 var user = [[${user}]];`
 var APP_PATH = [[${#request.getContextPath()}]];
 var LANG_COUNTRY = [[${#locale.getLanguage()+'_'+#locale.getCountry()}]];
 </script>
  • (3)标签引入的JS里面能使用内联表达式吗?答:不能!内联表达式仅在页面生效,因为Thymeleaf只负责解析一级视图,不能识别外部标签JS里面的表达式。

七、国际化

需要了解更多关于国际化的精彩描述请前往 SpringBoot 快速实现国际化i18n 。

例如在国际化文件中编写了user.title这个键值,然后使用#{}读取这个KEY即可获取翻译。

 <title th:text="#{user.title}">用户登陆</title>

八、详细教程

======== 有了上述基础后 下面正式开始Thymeleaf 的详细教程 ==============

首先通过Spring Initializr创建项目,如图所示:

然后在POM文件引入web 、thymeleaf等依赖:

<dependencies>
 <dependency><!--Web相关依赖-->
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency><!--页面模板依赖-->
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
 <dependency><!--热部署依赖-->
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-devtools</artifactId>
 <scope>runtime</scope>
 </dependency>
 </dependencies>

然后在src/main/resources/application.yml 配置页面路径:

spring:
 thymeleaf:
 cache: false #关闭缓存
 prefix: classpath:/views/ #调整页面路径

接着在src/main/java/com/hehe/web/UserController 获取用户信息:

@RestController
public class UserController {
 private List<User> userList = new ArrayList<>();
 {
 userList.add(new User("1", "socks", "123456", new Date()));
 userList.add(new User("2", "admin", "111111", new Date()));
 userList.add(new User("3", "jacks", "222222", null));
 }
 @GetMapping("/")
 public ModelAndView index() {
 return new ModelAndView("user/user", "userList", userList);
 }
}
public class User {
 private String id;
 private String username;
 private String password;
 private Date createTime;
 //请读者自行补充 构造器和 get/set方法..
}

开始编写公共页面:src/main/resources/views/common/head.html ,其中static为页面片段:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<!--声明static为页面片段名称-->
<head th:fragment="static">
 <link th:href="@{/webjars/bootstrap/css/bootstrap.css}" rel="stylesheet" type="text/css"/>
 <script th:src="@{/webjars/jquery/jquery.js}"></script>
</head>
</html>

接着编写用户列表页:src/main/resources/views/user/user.html 配合th:each显示用户列表信息。

使用说明:这里 th:replace="common/head::static" 表示将引用${spring.thymeleaf.prefix}/common/head.html的static页面片段,值得注意的是由于替换路径默认会拼接前缀路径,所以开头切勿在添加斜杠,否则在打包成JAR部署运行时将提示报Templates not found... 。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
 <meta charset="UTF-8"/>
 <title th:text="用户信息">User</title>
 <!--默认拼接前缀路径,开头请勿再添加斜杠,防止部署运行报错!-->
 <script th:replace="common/head::static"></script>
</head>
<body>
<div th:each="user,userStat:${userList}" th:class="${userStat.even}?'even':'odd'">
 序号:<input type="text" th:value="${userStat.count}"/>
 账号:<input type="text" th:value="${user.username}"/>
 密码:<input type="password" th:value="${user.password}"/>
 时间:<input type="text" th:value="${user.createTime}"/>
 时间:<input type="text" th:value="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}"/>
</div>
<script th:inline="javascript">
 //通过内联表达式获取用户信息
 var userList = [[${userList}]];
 console.log(userList)
</script>
</body>
</html>

然后编写单个用户页面:

至此大功告成,然后快速启动项目,如图所示:

快速启动项目

然后访问用户列表: http://localhost:8080 ,如图所示:

然后访问单个用户: http://localhost:8080/user/1 ,如图所示:

  • 入相关依赖
 <!--引入thymeleaf与Spring Security整合的依赖-->
 <dependency>
 <groupId>org.thymeleaf.extras</groupId>
 <artifactId>thymeleaf-extras-springsecurity4</artifactId>
 <version>3.0.2.RELEASE</version>
 </dependency>
 <!--引入Spring Security依赖-->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
 <!--引入Thymeleaf依赖-->
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
 </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 创建自定义WebSecurityConfigurerAdapter并重写configure方法
@EnableWebSecurity
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
 //拦截请求
 @Override
 protected void configure(HttpSecurity http) throws Exception {
 //设置哪些url允许被某种角色访问
 http.authorizeRequests().antMatchers("/").permitAll()
 .antMatchers("/bronze").hasRole("英勇黄铜")
 .antMatchers("/silver").hasRole("不屈白银")
 .antMatchers("/gold").hasRole("荣耀黄金")
 .antMatchers("/platinum").hasRole("华贵铂金")
 .antMatchers("/diamond").hasRole("璀璨钻石")
 .antMatchers("/master").hasRole("超凡大师")
 .antMatchers("/challenger").hasRole("最强王者");
 //启用登录功能,可以使用默认的登录页,这里使用自定义的login.html页面
 http.formLogin().loginPage("/login");
 //启用注销功能,(需要提供一个action为/logout的form)并设置注销后访问的url,这里注销后跳转到首页
 http.logout().logoutSuccessUrl("/");
 //启用rememberMe功能,将用户信息保存在cookie中
 http.rememberMe();
 }
 //授权认证
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
 //inMemoryAuthentication表示使用基于内存的验证,还可以使用基于数据库的验证等,使用BCrypt编码对密码进行加密
 //,否则报错java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("bronze")
 .password(new BCryptPasswordEncoder().encode("0110")).roles("英勇黄铜")
 .and().withUser("silver").password(new BCryptPasswordEncoder()
 .encode("0110")).roles("不屈白银").and().withUser("gold")
 .password(new BCryptPasswordEncoder().encode("0110")).roles("荣耀黄金")
 .and().withUser("platinum").password(new BCryptPasswordEncoder()
 .encode("0110")).roles("华贵铂金").and().withUser("diamond")
 .password(new BCryptPasswordEncoder().encode("0110")).roles("璀璨钻石")
 .and().withUser("master").password(new BCryptPasswordEncoder()
 .encode("0110")).roles("超凡大师").and().withUser("challenger")
 .password(new BCryptPasswordEncoder().encode("0110")).roles("最强王者");
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  • 主页显示
<div align="center" style="margin-top: 15px" sec:authorize="!isAuthenticated()">
 <h4 style="color: blue;">欢迎您,亲爱的召唤师!<a th:href="@{/login}"> 请登录</a></h4>
</div>
<div align="center" style="margin-top: 15px" sec:authorize="isAuthenticated()">
 <h4 style="color: blue;">召唤师 <span sec:authentication="name"></span>
 ! 您的段位为:<span sec:authentication="principal.authorities"></span>
 </h4>
 <form th:action="@{/logout}" method="post">
 <input type="submit" th:value="注销登录">
 </form>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('英勇青铜')">
 <a th:href="@{/bronze}">点击领取英勇青铜段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('不屈白银')">
 <a th:href="@{/silver}">点击领取不屈白银段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('荣耀黄金')">
 <a th:href="@{/gold}">点击领取荣耀黄金段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('华贵铂金')">
 <a th:href="@{/platinum}">点击领取华贵铂金段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('璀璨钻石')">
 <a th:href="@{/diamond}">点击领取璀璨钻石段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('超凡大师')">
 <a th:href="@{/master}">点击领取超凡大师段位奖励</a>
</div>
<div align="center" style="margin-top: 100px" sec:authorize="hasRole('最强王者')">
 <a th:href="@{/challenger}">点击领取最强王者段位奖励</a>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  • 点击领取奖励页面
<div align="center" style="margin-top: 20px">
 <a th:href="@{/}">返回首页</a>
</div>
<div align="center" style="margin-top: 100px">
 <h3>您在本赛季段位为:<span style="color: aqua;font-style: italic">英勇黄铜</span></h3>
 <h4>获得皮肤奖励:<span style="color: peru">锈迹斑斑 布里茨</span></h4>
</div>
1
2
3
4
5
6
7
8
  • 自定义登录页面
<div align="center" style="margin-top: 60px">
 <form th:action="@{/login}" method="post">
 <p>
 <label>Username</label>
 <input type="text" th:name="username">
 </p>
 <p>
 <label>Password</label>
 <input type="password" th:name="password">
 </p>
 <p>
 <label>Remember Me</label>
 <input type="checkbox" th:name="remember-me">
 </p>
 <div align="center">
 <input type="submit" th:value="登录">
 </div>
 </form>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
测试结果:
  • 首页


  • 登录页,点击Remember Me下次访问不需要重新登录


  • 登录成功


  • 奖励页面