thymeleaf
Thymeleaf是做页面渲染的。
Thymeleaf 需要你在 Servlet(或者 Spring MVC Controller)里调用它的 API 来做页面渲染。
作用
- 替代 JSP:解决 JSP 在开发体验上的不足,比如和 HTML 混写后难以在前端工具中直接预览。
- 天然支持 HTML:Thymeleaf 的模板就是合法的 HTML 文件,可以直接在浏览器中打开预览,而 JSP 必须运行在服务器中才行。
- 更强的标签表达式:比如 EL 表达式、条件渲染、循环渲染等。
- 和 Spring MVC 无缝集成(常见用法):用作前端视图层技术。
MVC概念
M:Model模型
V:View视图
C:Controller控制器
MVC是在表述层开发中运用的一种设计理念。主张把封装数据的『模型』、显示用户界面的『视图』、**协调调度的『控制器』**分开。
好处:
- 进一步实现各个组件之间的解耦
- 让各个组件可以单独维护
- 将视图分离出来以后,我们后端工程师和前端工程师的对接更方便
MVC和三层架构之间关系
简介
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP,Velocity,FreeMaker等, 它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。它的主要作用是在静态页面上渲染显示动态数据。
优势
-
SpringBoot官方推荐使用的视图模板技术,和SpringBoot完美整合。
-
不经过服务器运算仍然可以直接查看原始值,对前端工程师更友好。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${username}">Original Value</p>
</body>
</html>
物理视图和逻辑视图
在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是物理视图。
/pages/user/login_success.html
如果我们把所有的HTML页面都放在某个统一的目录下,那么转发地址就会呈现出明显的规律:
/pages/user/login.html
/pages/user/login_success.html
/pages/user/regist.html
/pages/user/regist_success.html
……
路径的开头都是:/pages/user/,路径的结尾都是:.html。
所以,路径开头的部分我们称之为视图前缀,路径结尾的部分我们称之为视图后缀。
逻辑视图
物理视图=视图前缀+逻辑视图+视图后缀
视图前缀 | 逻辑视图 | 视图后缀 | 物理视图 |
---|---|---|---|
/pages/user/ | login | .html | /pages/user/login.html |
/pages/user/ | login_success | .html | /pages/user/login_success.html |
基本使用
1、加入jar包
2、配置上下文参数
<!-- 在上下文参数中配置视图前缀和视图后缀 -->
<context-param>
<param-name>view-prefix</param-name>
<param-value>/WEB-INF/view/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
说明:param-value中设置的前缀、后缀的值不是必须叫这个名字,可以根据实际情况和需求进行修改。
模版页面位置
为什么要放在WEB-INF目录下?
原因:WEB-INF目录不允许浏览器直接访问,所以我们的视图模板文件放在这个目录下,是一种保护。以免外界可以随意访问视图模板文件。
访问WEB-INF目录下的页面,都必须通过Servlet转发过来,简单说就是:不经过Servlet访问不了。
这样就方便我们在Servlet中检查当前用户是否有权限访问。
那放在WEB-INF目录下之后,重定向进不去怎么办?
重定向到Servlet,再通过Servlet转发到WEB-INF下。
视图前缀和后缀
当我们把Thymeleaf的视图模板文件统一存放在WEB-INF/pages目录下时,它们转发的路径就有规律了
- 路径开头:都是/WEB-INF/pages/(正好是我们设置的viewPrefix),例如:/WEB-INF/pages/
- 路径结尾:都是.html(正好是我们设置的viewSuffix)例如:.html
- 物理视图:完整的转发路径,例如:/WEB-INF/pages/apple.html
- 逻辑视图:去除前缀、后缀之后剩余的部分,例如:apple
3、创建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());
}
}
3、两种基类(新)
package com.atguigu.demo.servlet.parent;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.WebApplicationTemplateResolver;
import org.thymeleaf.web.IWebApplication;
import org.thymeleaf.web.servlet.IServletWebExchange;
import org.thymeleaf.web.servlet.JakartaServletWebApplication;
import java.io.IOException;
import java.lang.reflect.Method;
public class ServletParent extends HttpServlet {
private TemplateEngine templateEngine;
// view:视图
// prefix:前缀
private String viewPrefix = "/WEB-INF/pages/";
// suffix:后缀
private String viewSuffix = ".html";
@Override
public void init() throws ServletException {
// 1.获取ServletContext对象
ServletContext servletContext = this.getServletContext();
// 2.创建Thymeleaf解析器对象
IWebApplication webApplication = JakartaServletWebApplication.buildApplication(servletContext);
WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(webApplication);
// 3.给解析器对象设置参数
// ①HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
// ②设置视图前缀
templateResolver.setPrefix(viewPrefix);
// ③设置视图后缀
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对象
JakartaServletWebApplication jakartaServletWebApplication = JakartaServletWebApplication.buildApplication(getServletContext());
IServletWebExchange webExchange = jakartaServletWebApplication.buildExchange(req, resp);
WebContext webContext = new WebContext(webExchange, req.getLocale(), jakartaServletWebApplication.getAttributeMap());
// 3.处理模板数据
templateEngine.process(templateName, webContext, resp.getWriter());
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String requestURI = request.getRequestURI();
if (requestURI.contains(";")) {
requestURI = requestURI.substring(0, requestURI.indexOf(";"));
}
String[] split = requestURI.split("/");
String methodName = split[split.length - 1];
Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
method.setAccessible(true);
method.invoke(this, request, response);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
4、创建page1.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>Hello yuluo,have fun,good day for you!</h3>
</body>
</html>
5、创建Servlet
<!--配置first Servlet-->
<servlet>
<servlet-name>firstServlet</servlet-name>
<servlet-class>com.fruit.yuluo.servlet.firstServlet.firstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>firstServlet</servlet-name>
<url-pattern>/firstReq</url-pattern>
</servlet-mapping>
自定义Servlet让其继承ViewBaseServlet
package com.fruit.yuluo.servlet.firstServlet;
import com.fruit.yuluo.servlet.ViewBaseServlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class firstServlet extends ViewBaseServlet {
// 重写 HttpServlet 方法
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 之前渲染方式:request.getRequestDispatcher("index.html").forward(request,response);
// 现在使用 thymeleaf 渲染:super.processTemplate("逻辑视图名称",request,response);
// 物理视图名称 = 视图前缀 + 逻辑视图名称 + 视图后缀
// /page01.html= / page01 .html
super.processTemplate("page1",req,resp);
}
}
基本语法
th名称空间
循环遍历
<!-- th:each 表示准备迭代 -->
<!-- ${} 这是thymeleaf的语法,表示thymeleaf表达式 -->
<!-- session.key 相当于 session.getAttribute(key) -->
<tr th:each="item ${session.keyList}">
<td th:text="${item.fname}">苹果</td>
<td th:text="${item.price}">5</td>
<td th:text="${item.fcont}">2</td>
</tr>
th:*语法
$ 是thymeleaf的语法,表示thymeleaf表达式,里边写 thymeleaf 表达式
普通字符串
<p th:text="标签体新值">标签体原始值</p>
<p th:text="'Hello World'"></p>
<!--外层 ' ' 包裹普通字符串 -->
<!--渲染后:-->
<p>Hello World</p>
th:text作用
- 不经过服务器解析,直接用浏览器打开HTML文件,看到的是标签体原始值
- 经过服务器解析,Thymeleaf引擎根据th:text