看一下官网的解释:
Thymeleaf是⾯向Web和独⽴环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚⾄纯⽂本。
Thymeleaf旨在提供⼀个优雅的、⾼度可维护的创建模板的⽅式。 为了实现这⼀⽬标,Thymeleaf建⽴在⾃然模板的概念上,将其逻辑注⼊到模板⽂件中,不会影响模板设计原型。 这改善了设计的沟通,弥合了设计和开发团队之间的差距。
Thymeleaf从设计之初就遵循Web标准——特别是HTML5标准 ,如果需要,Thymeleaf允许您创建完全符合HTML5验证标准的模板。
看说明,可以得出好像说了什么,又好像什么都没有说。因为我也不知道说什么,不过 SpringBoot推荐的模板引擎就是Thymeleaf。
不过现在不结合SpringBoot使用这个模板。直接同前面所学的servlet进行整合来使用,这样虽然麻烦点,但是至少学习这个模板,不会对其它的基础有要求。
搭建环境
依赖搭建
这个可以下载jar包,或者使用maven进行依赖环境配置:
搞这个需要的两个jar 包,既然是javaweb项目,所以必然有servlet这个jar包,还有一个thymeleaf的jar包:
servlet-api-***.jar
thymeleaf*.*.**.RELEASE
其中* 是数字表示的是版本号,所以不再多说什么。
如果使用maven配置:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
然后在IDE中创建一个Javaweb项目。
如果不知道如何在IDE中创建web项目:
- 如果不会,想创建常用的web项目可以看另一篇文章:传送阵
- 如果不会,想创建maven的web项目可以看另一篇文章:传送阵
配置解析器
既然启动Thymeleaf模板,那就是有要解析这个模板的类,看一下官网解释:
除了解释器,还有对 模版引擎的配置,这些就看了,一般会通过配置一个父类的servlet对加载的thymeleaf模板页面进行处理。然后自己的逻辑的servlet继承这个父类即可。
所以创建一个父类的servlet:
package com.xzd.servlet;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ViewBaseServlet extends HttpServlet {
private TemplateEngine templateEngine;
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
// ④设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
// ⑤设置是否缓存
templateResolver.setCacheable(true);
// ⑥设置服务器端编码方式
templateResolver.setCharacterEncoding("utf-8");
// 4.创建模板引擎对象
templateEngine = new TemplateEngine();
// 5.给模板引擎对象设置模板解析器
templateEngine.setTemplateResolver(templateResolver);
}
protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1.设置响应体内容类型和字符集
resp.setContentType("text/html;charset=UTF-8");
// 2.创建WebContext对象
WebContext webContext = new WebContext(req, resp, getServletContext());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
}
同事还需与在web.xml中配置一些参数来满足:
// ②设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
// ③设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
具体配置如下:
<!-- 在servletContext中配置上下文参数 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
简单例子演示
先看一下整体结构
首先创建逻辑servlet类,其基础父类的servlet
//通过注释而配置servlet的 url 这样可以更加直观看web.xml中为thymeleaf做了什么
@WebServlet("/testservelt")
public class testservlet extends ViewBaseServlet{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession httpSession= req.getSession();
List list=new ArrayList();
list.add("战神");
list.add("艾尔登法环");
list.add("卧龙苍天陨落");
list.add("霍格沃茨:遗产");
httpSession.setAttribute("list",list);
// //此处的视图名称是 index
// //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
// //逻辑视图名称 : testservlet 为了一致所以 html和servlet名字一样,当然也可以不一样
// //物理视图名称 : view-prefix + 逻辑视图名称 + view-suffix
// //所以真实的视图名称是: /WEB-INF/html/ testservlet .html
super.processTemplate("testservlet",req,resp);
}
}
看一下web.xml中的配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 在servletContext中配置上下文参数 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/html/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
</web-app>
然后写一个页面,不要关心thymeleaf的语法问题,主要是为了演示:
<!DOCTYPE html>
<!--在页面上外部导入thymeleaf-->
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table {
border-collapse: collapse;
}
table, td, th {
border: 1px solid black;
}
#gameid {
margin: 12px auto;
collapse: 1px;
}
td {
text-align: center;
width: 160px;
}
</style>
</head>
<body>
<table id="gameid">
<tr>
<th class="w20">游戏名</th>
<th>操作</th>
</tr>
<tr th:if="${#lists.isEmpty(session.list)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.list)}" th:each="gamename : ${session.list}">
<td><a th:text="${gamename}" href="#">默认没有如果不是通过服务器启动,就显示这个文本</a></td>
<td><a th:text="购买" href="#">默认没有如果不是通过服务器启动,就显示这个文本</a></td>
</tr>
</table>
</body>
</html>
为了方便,在index.jsp中写一个转发:
<html>
<body>
<h2>Hello World!</h2>
<%--转发到自己配置的url上--%>
<jsp:forward page="/testservelt"></jsp:forward>
</body>
</html>
然后访问:http://localhost:8080/thymeleaf_web/
然后看一下结果:
只要有例子了,后面就直接聊语法了,因为按照上面配置环境肯定没问题。
常用语法
Thymeleaf 模板引擎支持多种表达式:
- 变量表达式:${…}
- 选择变量表达式:*{…}
- 链接表达式:@{…}
- 国际化表达式:#{…}
- 片段引用表达式:~{…}
表达式语法
th:text
th:text 是用来修改标签文本值,比如:
<p th:text="th表达式text的值">P标签体原始值</p>
其实例子中也可以看出其在演示时候输出的是th:text的值。不过需要注意其使用不同,显示的值也是不同的。
- 不经过服务器解析,直接用浏览器打开HTML文件,看到的是标签中间文本。
- 经过服务器解析,Thymeleaf引擎根据th:text属性指定的文本会替换标签中间的文本。
th:value
修改value属性值,比如:
<input type="text" th:value="1111111" value="2222222">
也是如果是服务器启动页面显示是1111111,如果是普通浏览器打开那就是2222222。
链接表达式 @{ }
意思是解析URL地址,还是演示:
<p th:text="@{/aaa/bbb/ccc}"></p>
但是IDE会提示报错,不过不会影响运行结果;
看一下结果就明白了:
会自动在前面添加项目名字,因为我创建的项目名字就是thymeleaf_web。
这个语法的好处是:实际开发过程中,项目在不同环境部署时,Web应用的名字有可能发生变化。所以上下文路径不能写死。而通过@{}动态获取上下文路径后,不会出错误。
操作域数据
对于web项目中的域不了解的话可以看一下,前面的文章:传送阵
直接演示常用的请求域,会话域,以及应用域。
首先咋servelt中创建数据
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("request_data", "request_data");
HttpSession httpsession= req.getSession();
httpsession.setAttribute("session_data","session_data");
ServletContext servletContext=getServletContext();
servletContext.setAttribute("application_data","application");
super.processTemplate("testservlet",req,resp);
}
}
然后再页面如此操作:
<p th:text="${request_data}"> </p>
<p th:text="${session.session_data}"> </p>
<p th:text="${application.application_data}"> </p>
可以看出后台传递的数据,再thymeleaf中呈现文本显示的适合其值格式如下:
th:text="${}"
获取请求参数 param
得到url中的参数,这个还是来一个例子:
http://localhost:8080/thymeleaf_web/?username=张三&favourite=篮球&favourite=rap
然后看例子:
<p th:text="${param.username}"> </p>
<p th:text="${param.favourite}"> </p>
<p th:text="${param.favourite[0]}"> </p>
<p th:text="${param.favourite[1]}"> </p>
看结果就明白了。
内置对象
jsp中有内置对象,对于thymeleaf 也不例外,这个直接列举了。
Thymeleaf 中常用的内置基本对象如下:
- #ctx :上下文对象;
- #vars :上下文变量;
- #locale:上下文的语言环境;
- #request:HttpServletRequest 对象(仅在 Web 应用中可用);
- #response:HttpServletResponse 对象(仅在 Web 应用中可用);
- #session:HttpSession 对象(仅在 Web 应用中可用);
- #servletContext:ServletContext 对象(仅在 Web 应用中可用)。
来一个例子:
<p th:text="${#request.getContextPath()}"></p>
<p th:text="${#request.getAttribute('请求域中数据值')}"></p>
除了能使用内置的基本对象外,变量表达式还可以使用一些内置的工具对象。
- strings:字符串工具对象,常用方法有:equals、equalsIgnoreCase、length、trim、toUpperCase、toLowerCase、indexOf、substring、replace、startsWith、endsWith,contains 和 containsIgnoreCase 等;
- numbers:数字工具对象,常用的方法有:formatDecimal 等;
- bools:布尔工具对象,常用的方法有:isTrue 和 isFalse 等;
- arrays:数组工具对象,常用的方法有:toArray、length、isEmpty、contains 和 containsAll 等;
- lists/sets:List/Set 集合工具对象,常用的方法有:toList、size、isEmpty、contains、containsAll 和 sort 等;
- maps:Map 集合工具对象,常用的方法有:size、isEmpty、containsKey 和 containsValue 等;
- dates:日期工具对象,常用的方法有:format、year、month、hour 和 createNow 等。
来一个例子:
<p th:text="${#dates.createNow()}"></p>
公共页面抽取
抽取公共页面 th:fragment
使用th:fragment来给这个片段命名:
先创建一个test.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:fragment="fragment_name" id="fragment_id">
<span>抽取的啊公共页面片段</span>
</div>
</body>
</html>
具体路径是:
引用公共页面
在 Thymeleaf 中,我们可以使用以下 3 个属性,将公共页面片段引入到当前页面中。
- th:insert:将代码块片段整个插入到使用了 th:insert 属性的 HTML 标签中;
- th:replace:将代码块片段整个替换使用了 th:replace 属性的 HTML 标签中;
- th:include:将代码块片段包含的内容插入到使用了 th:include 属性的 HTML 标签中。
使用上 3 个属性引入页面片段,都可以通过以下 2 种方式实现。
- ~{templatename::selector}:模板名::选择器
- ~{templatename::fragmentname}:模板名::片段名
通常情况下,~{} 可以省略,其行内写法为 [[~{...}]] 或 [(~{...})],其中 [[~{...}]] 会转义特殊字符,[(~{...})] 则不会转义特殊字符。
现在演示一下:
<p> 这个是调用的页面啊</p>
<!--th:insert 片段名引入-->
<div th:insert="/utils/test::fragment_name"></div>
<!--th:insert id 选择器引入-->
<div th:insert="/utils/test::#fragment_id"></div>
补充 :直接执行表达式
这个需要借助一下serlvet才行,所在servert中配置:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("htmllabel", "<span>测试</span>");
super.processTemplate("testservlet",req,resp);
}
}
然后再html中如此写:
<p>有转义效果:[[${htmllabel}]]</p>
<p>无转义效果:[(${htmllabel})]</p>
先看结果:
可以看出如果传递的数据带有html标签要求可以在页面。
[[${ 内容 }]]: 显示完整的数据,哪怕是标签也会当作文本显示
[(${ 内容 })] 会让传递数据中的html标签其效果
分支与迭代
既然是模板,那肯定也会又逻辑判断以及迭代的语句,所以下面简单的聊一下。
分支
if和unless
让标记了th:if、th:unless的标签根据条件决定是否显示。
- th:if: 满足条件执行
- th:unless: 不满足条件执行
其实这个两个例子前面例子演示了,还是直接黏贴过来:
<tr th:if="${#lists.isEmpty(session.list)}">
<td colspan="4">对不起,库存为空!</td>
</tr>
<tr th:unless="${#lists.isEmpty(session.list)}" th:each="gamename : ${session.list}">
简单理解可以将th:if和th:unless 理解为 java中的 if ------if not-----
其中的th: if还可以搭配not关键字使用,比如上面th:unless可以如下写:
<tr th:unless="${#lists.isEmpty(session.list)}"></tr>
替代为:
<tr th:if="${not #lists.isEmpty(session.list)}">></tr>
switch
有if的必然有这个关键字。
th:switch 与 Java 的 switch case语句类似通常与 th:case 配合使用,根据不同的条件展示不同的内容
还是直接演示:
<div th:switch="${session.game.id}">
<p th:case=" 1">艾尔登法环</p>
<p th:case=" 2">战神</p>
</div>
迭代
th:each 遍历,支持 Iterable、Map、数组等。
这个其实在演示的例子中用了:
<tr th:unless="${#lists.isEmpty(session.list)}" th:each="gamename : ${session.list}">
<td><a th:text="${gamename}" href="#">默认没有如果不是通过服务器启动,就显示这个文本</a></td>
<td><a th:text="购买" href="#">默认没有如果不是通过服务器启动,就显示这个文本</a></td>
当然thymeleaf的语法还有很多,现在不过多阐述了,可以具体使用的时候可以聊,同时还有一点那就是如何整合springmpv,springboot等,这些再用的时候演示如何配置,这里就不再多说了。