比jsp好用一点,因为jsp每次都要编译成servlet,效率实在太低了。但是现在也没太多人用了,毕竟都是前后端分离,但是作为学习路线,还是不跳过的好,简单了解一下。
快速使用
新建SpringBoot项目的时候除了勾选Spring Web,还要勾选Thymeleaf,其实也就是pom文件里多一个Thymeleaf的起步依赖,然后就可以用了。
首先还是需要一个controller
@Controller("myController")
public class MyController {
@RequestMapping("/message")
public ModelAndView getMessage(ModelAndView modelAndView) {
modelAndView.addObject("message", "展示后台数据");
modelAndView.setViewName("message");
return modelAndView;
}
}
然后现在就不像之前使用jsp那么麻烦了,直接在templates目录下新建message.html就可以了。
message.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- thymeleaf模板引擎的页面必须通过中央调度器-->
<h3 th:text="${message}">展示前端数据</h3>
</body>
</html>
只需要在命名空间中引入thymeleaf就可以正常使用了,所有功能基本都要th:开头。模板引擎的一个好处就是当可以拿到message的数据就替换“展示前端数据”这句话, 如果拿不到就不替换,不会什么都不显示。
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128112340.png)
关闭页面缓存
上面的例子和之前不太一样的是,没有配视图解析器,那我们没有加.html后缀,SpringBoot是怎么识别的?其实已经配了,用的是默认的值:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128112803.png)
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128112831.png)
所以自己可加可不加,后续如果页面的位置需要改动的时候记得改就可以。
之前用jsp的时候我们只要在配置中这么设置就可以修改html的时候页面自动更新:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128113332.png)
但是Thymeleaf自带的有缓存,而且默认开启,所以需要把缓存也关了才可以:
spring.thymeleaf.cache=false
这样修改html就不用重新启动项目了。
变量表达式
一种就是${},就是之前的EL表达式,用起来也比较方便;还有一种是选择变量表达式*{},感觉看起来不太直观。
@RequestMapping("/user")
public ModelAndView getUserDetail(ModelAndView modelAndView) {
modelAndView.addObject("user", new User(1001, "张三", 23));
modelAndView.setViewName("user");
return modelAndView;
}
user.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>变量表达式</title>
</head>
<body>
<h1>标准变量表达式:${} (推荐)</h1>
用户编号:<span th:text="${user.id}"></span><br>
用户姓名:<span th:text="${user.name}"></span><br>
用户年龄:<span th:text="${user.age}"></span><br>
<h1>选择变量表达式:*{} (不推荐)</h1>
<div th:object="${user}">
用户编号:<span th:text="*{id}"></span><br>
用户姓名:<span th:text="*{name}"></span><br>
用户年龄:<span th:text="*{age}"></span><br>
</div>
</body>
</html>
结果:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128131446.png)
有可能会碰到变量表达式爆红的情况,但是不影响运行结果。我现在试下来是先写controller的代码,然后再创建对应的页面,就不会爆红;但是先创建页面,再写代码,就会爆红,但最后页面都能拿到数据,还挺玄学的。
路径表达式
格式:@{…}。为了后续演示方便,把context-path设为了/thymeleaf
controller中直接将/url请求转发到url.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>URL路径表达式</title>
</head>
<body>
<h1>URL路径表达式@{...}</h1>
<h2>绝对路径</h2>
<a th:href="@{www.baidu.com}">绝对路径,跳转到百度</a><br>
<h2>相对路径</h2>
<a th:href="@{/user}">相对路径,/ 代表项目上下文</a>
</body>
</html>
可以直接看网页源代码,看路径表达式最后变成什么样子了:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128140849.png)
这个时候www.baidu.com是没有带协议的,下面点一下:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128141046.png)
可以看到还是用项目的上下文当前缀,也就是这里并不是绝对路径,绝对路径一定是带协议和端口号才可以的。
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128141215.png)
html中加上协议就可以正常跳转了,这个时候绝对路径和正常的超链接标签是一样的,没什么区别。
但是这个时候看/user在页面中直接变成了/thymeleaf/user,加上了项目的contexpath,点击可以正常跳转到user页面:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128141405.png)
地址栏的url为:http://localhost:8080/thymeleaf/user
如果要跳转到另一个服务器上的另一个项目,就不能用 / 了,这只能在当前项目跳转,thymeleaf提供了另一种语法:
<h2>相对服务器根路径</h2>
<a th:href="@{~/anotherProjectContext/user}">相对服务器根路径,~代表服务器的根</a>
再看一下源代码:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128142030.png)
点击的话地址栏的url会变为http://localhost:8080/anotherProjectContext/user
下面是带参数的写法,这个时候就体现出地址表达式的强大了,先改一下代码:
@RequestMapping("/url")
public ModelAndView getURL(ModelAndView modelAndView) {
modelAndView.addObject("id", 1001);
modelAndView.addObject("name", "李四");
modelAndView.addObject("age", 24);
modelAndView.setViewName("url");
return modelAndView;
}
@RequestMapping("/ret")
@ResponseBody
public String getRet(Integer id, String name, Integer age) {
return "请求路径中带的参数,id = " + id + ", name = " + name + ", age = " + age;
}
<h2>相对路径带参数</h2>
<a th:href="@{/ret(id = ${id}, name = ${name}, age = ${age})}">跳转到/ret,并且带多个参数</a>
这个时候就不需要像以前那样自己去拼字符串了,直接按这个格式写就行,下面看一下网页源代码:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128143146.png)
可以看到thymeleaf已经帮我们拼好了,直接点是没有任何问题的:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128143220.png)
如果觉得这种传参方式不够REST,thymeleaf也是支持REST风格的,写法如下:
@RequestMapping("/rest/{id}/{name}/{age}")
@ResponseBody
public String getRest(@PathVariable("id") Integer id,
@PathVariable("name") String name,
@PathVariable("age") Integer age) {
return "请求路径中带的参数,id = " + id + ", name = " + name + ", age = " + age;
}
<h2>相对路径带参数REST风格</h2>
<a th:href="@{/rest/{id}/{name}/{age}(id = ${id}, name = ${name}, age = ${age})}">跳转到/rest,带多个参数</a>
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128143759.png)
当然实际也没有这么用REST风格的,这里只是演示一下参数是可以这么传递的,不用拼在url后面:
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128144019.png)
静态资源
这个问题卡了半天,首先还是要注意maven的问题,compile之后看一下classes目录下有没有你的静态资源,如果没有clean一下或者rebuild一下,参考之前的博客。
图片的路径是/static/img/…png,这里用不用th标签都可以,最起码我这个版本,这两种写法还都访问的到图片
<h2>静态资源路径</h2>
<img src="img/wallhaven-e7ypv8_1920x1080.png" alt="加载失败">
<img th:src="@{/img/wallhaven-e7ypv8_1920x1080.png}" alt="加载失败">
![](https://cdn.jsdelivr.net/gh//Fushier/ImgCloud@main/data/20210128152257.png)
![](https://github.com/Fushier/ImgCloud/blob/main/data/20210128152405.png?raw=true)
网上还有在配置文件中这样配的
spring.resources.static-locations=classpath:/static/
spring.mvc.static-path-pattern=/static/**
这样配的话,路径就是/static/img/…png。两种写法我这都可以。
常用标签
剩下的了解一下就行,毕竟后面也用得不多。
-
遍历(array,List,Map都一样)
@RequestMapping("/each/list") public ModelAndView eachList(ModelAndView mv) { List<User> userList = new ArrayList<>(); for (int i = 0; i < 10; ++i) { User user = new User(100 + i, "张" + i, 20 + i); userList.add(user); } mv.addObject("userList", userList); mv.setViewName("each"); return mv; }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>循环遍历List集合</title> </head> <body> <!-- user 当前循环的对象变量名称 userStat 当前循环对象状态的变量,可以不写,默认就是对象变量名+Stat ${userList} 当前循环的集合 --> <div th:each="user, userStat : ${userList}"> <span th:text="${userStat.size}"></span> <span th:text="${userStat.count}"></span> <span th:text="${userStat.index}"></span> <span th:text="${user.id}"></span> <span th:text="${user.name}"></span> <span th:text="${user.age}"></span> </div> </body> </html>
-
条件判断
@RequestMapping("/condition") public ModelAndView condition(ModelAndView modelAndView) { modelAndView.addObject("sex", 1); modelAndView.setViewName("condition"); return modelAndView; }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>条件判断</title> </head> <body> <h1>if</h1> <div th:if="${sex == 1}">男</div> <div th:if="${sex == 0}">女</div> </body> </html>
还有一个unless,就是括号里的条件不成立才执行,不演示了。
-
内敛表达式
@RequestMapping("/inline") public ModelAndView inline(ModelAndView mv) { mv.addObject("data", "inlineData"); mv.setViewName("inline"); return mv; }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>内敛表达式</title> </head> <body> <h1>内敛文本 th:inline="text"</h1> <div th:inline="text">数据:[[${data}]]</div> <h1>内敛脚本 th:inline="javascript"</h1> <script type="text/javascript" th:inline="javascript"> function showData() { alert([[${data}]]) } </script> <button onclick="showData()">展示数据</button> </body> </html>
-
拼接字符串
就不演示了,也很直观
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>拼接字符串</title> </head> <body> <div th:text="|共${totalPage}页,当前第${currentPage}页。|"></div> </body> </html>
这样就把从后端拿到的数据直接和前端的字符串拼一块了,不用自己再用单引一段一段地拼了。
-
内置对象
thymeleaf内置了很多对象,和jsp一样,可以直接拿来用,但是我们用的最多的还是request,response,session这三个。thymeleaf使用这些内置对象需要在前面加 # 。
比如我们在controller中这么写request.getSession().setAttribute(“data”, “sessionData”)。那么在html中就可以${#session.data}这样直接取出来,还是非常方便的。
剩下的还有很多比如#dates是java.util.Date对象的实用方法,可以直接#date.format(date, pattern)。#strings是字符串对象的使用方法,可以#strings.contains() #strings.subString()。可以理解为工具类,具体用到什么再去查就可以。