- 如上图所示, 当用servlet实现动态页面展示的时候,虽然我们可以用writer.write()写出整个页面,以展示动态信息,
但是代码实在是太冗长,根本无法维护,所以需要将视图展示抽取出来,单独作为一个View视图层。
- 如果只使用HTML作为视图的话,它是无法展示动态数据的,所以我们对HTML产生了新的需求:既能够正常显示页面,又能在页面中包含动态数据部分。而动态数据单靠HTML本身是无法做到的,所以此时需要引入服务器端动态视图模板技术,如Thymeleaf。
Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似的模板引擎还有 JSP,Velocity,FreeMarker等, 它们也可以轻易的与Spring MVC等Web框架进行集成并作为Web应用的模板引擎。
模板引擎的主要作用是在静态页面上渲染显示动态数据。
Thymeleaf的优势:
-
是SpringBoot官方推荐使用的视图模板技术,和SpringBoot完美整合。
-
不经过服务器运算仍然可以直接查看原始值,对前端工程师更友好。
什么是物理视图?
在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 |
2. 表达式语法 2.1. 修改标签文本值注: 如果是thymeleaf的模板html文件,在html标签内要加入Thymeleaf的th名称空间的声明,否则模板文件不生效。如下所示:
代码:
标签体原始值
示例:
目标页面
username
th:text作用:
- 不经过服务器解析,直接用浏览器打开HTML文件,看到的是『标签体原始值』
- 经过服务器解析,Thymeleaf引擎根据th:text属性指定的『标签体新值』去替换『标签体原始值』
2.2. 修改指定属性值
代码:
示例:
目标页面
语法:任何HTML标签原有的属性,前面加上『th:』就都可以通过Thymeleaf来设定新值。
2.3. 解析URL地址
代码:
访问index.html
示例:
目标页面
访问index.html
经过解析后得到:
/thymeleaf_learn/index.html
所以@{/}的作用是在字符串前附加『上下文路径』
2.4. 解析携带参数的URL地址这个语法的好处是:实际开发过程中,项目在不同环境部署时,Web应用的名字有可能发生变化。所以上下文路径不能写死。而通过@{/} 动态获取上下文路径后,不管怎么变都不怕啦!
代码:
访问index.html
示例:
目标页面
访问index.html
2.5. 显示动态的首页
首页地址使用URL地址解析
一般项目启动后,默认访问的页面是位于项目目录下的index.html:
同时我们也可以在浏览器中直接访问index.html本身,那么index.html是不需要通过Servlet,当然也不经过模板引擎,所以index.html上的Thymeleaf的任何表达式都不会被解析。
目前访问主页的效果, 如下图所示:
解决办法:
- 通过Servlet访问index.html,这样就可以让模板引擎渲染页面了.
- 需要在view文件夹中建立一个新的模板文件作为首页,并为之建立一个Servlet,然后在web.xml配置文件中注册这个Servlet.
- 最后修改默认的欢迎界面配置为默认去访问这个Servlet即可。
而通过使用Servlet+Thymeleaf更改和动态解析首页地址,现在访问主页的效果如下:
进一步的好处:
通过上面的例子我们看到,所有和业务功能相关的请求都能够确保它们通过Servlet来处理,这样就方便我们统一对这些请求进行特定规则的限定。
具体操作步骤如下:
第一步,在view文件夹中创建一个新的动态首页hello.html:
hello.html
动态index页面
value
第二步,servlet目录下创建一个Servlet文件: IndexThymeleafServlet:
ViewbaseServlet
package com.carson.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//类继承处理模板文件的ViewbaseServlet
public class IndexThymeleafServlet extends ViewbaseServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//在请求域中插入数据,属性名:value,属性值: I am a dynamic index page.
req.setAttribute("value","I am a dynamic index page.");
//请求转发跳转到/WEB-INF/view/hello.html
processTemplate("hello",req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
第三步,修改web.xml注册Servlet, 并把默认的首页指向servlet:
indexThymeleafServlet
com.carson.servlet.IndexThymeleafServlet
indexThymeleafServlet
/indexThymeleaf
indexThymeleaf
最后启动Tomcat测试: 发现默认访问的不是index.html了,而是hello.html
2.6. 域对象在Thymeleaf中的使用 2.6.1 什么是域对象
域对象是在服务器中有一定作用域范围的对象,在这个范围内的所有动态资源都能够共享域对象中保存的数据。
请求域request在请求转发的场景下,我们可以借助HttpServletRequest对象内部给我们提供的存储空间,帮助我们携带数据,把数据发送给转发的目标资源。
请求域:HttpServletRequest对象内部给我们提供的存储空间
会话域session会话域的范围是一次会话.
会话: 用户打开一个浏览器,访问多个web资源,关闭浏览器,这个过程称之为一个会话。
应用域application应用域的范围是整个项目全局, 也就是ServletContext对象, 其凌驾于各个Servlet之上。
2.6.2. 在Thymeleaf中操作域对象我们通常的做法是,在Servlet中将数据存储到域对象中,而在使用了Thymeleaf的前端页面中取出域对象中的数据进行展示。
操作请求域Servlet中代码:
String requestAttrName = "helloRequestAttr"; String requestAttrValue = "helloRequestAttr-VALUE"; request.setAttribute(requestAttrName, requestAttrValue);
Thymeleaf表达式:
操作会话域request field value
Servlet中代码:
// ①通过request对象获取session对象
HttpSession session = request.getSession();
// ②存入数据
session.setAttribute("helloSessionAttr", "helloSessionAttr-VALUE");
Thymeleaf表达式:
操作应用域这里显示会话域数据
Servlet中代码:
// ①通过调用父类的方法获取ServletContext对象
ServletContext servletContext = getServletContext();
// ②存入数据
servletContext.setAttribute("helloAppAttr", "helloAppAttr-VALUE");
Thymeleaf表达式:
2.7. 获取请求参数这里显示应用域数据
当一个带参数的链接指向一个Servlet,那么该Servlet该如何获取参数,并显示到Thymeleaf的页面中呢?
根据一个参数名获取一个参数值代码:
这里替换为请求参数的值
示例:
目标页面
这里替换为请求参数的值
页面显示效果:
根据一个参数名获取多个参数值代码:
这里替换为请求参数的值
页面显示效果:
如果想要精确获取某一个值,可以使用数组下标。页面代码:
这里替换为请求参数的值
这里替换为请求参数的值
页面显示效果:
2.8. 内置对象所谓内置对象其实就是在Thymeleaf的表达式中可以直接使用的对象。
基本内置对象用法举例:
表达式的基本内置对象
调用#request对象的getContextPath()方法
调用#request对象的getAttribute()方法,读取属性域
基本思路:
- 如果不清楚这个对象有哪些方法可以使用,那么就通过getClass().getName()获取全类名,再回到Java环境查看这个对象有哪些方法
- 内置对象的方法可以直接调用
- 调用方法时需要传参的也可以直接传入参数
Servlet中将List集合数据存入请求域:
request.setAttribute("aNotEmptyList", Arrays.asList("aaa","bbb","ccc"));
request.setAttribute("anEmptyList", new ArrayList<>());
模板页面代码:
#list对象isEmpty方法判断集合整体是否为空aNotEmptyList:测试#lists
#list对象isEmpty方法判断集合整体是否为空anEmptyList:测试#lists
公共内置对象对应的源码位置:
2.9. 分支与迭代 2.9.1 if 和unless分支标记了th:if、th:unless的标签会根据条件决定是否显示标签。
th:if 如果值为true 则显示出该标签 否则 不显示(隐藏)
th:unless则和th:if 相反
示例的Servlet代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//往request域对象中存储List集合
request.setAttribute("aNotEmptyList", Arrays.asList("aaa","bbb","ccc"));
request.setAttribute("anEmptyList", new ArrayList<>());
//调用方法渲染视图
processTemplate("list", request, response);
}
示例的HTML代码:
list.html
2.9.2. switch分支anNotEmptyList中有数据
anNotEmptyList中没有数据
anEmptyList中有数据
anEmptyList中没有数据
th:switch 和 th:case 与 Java 中的 switch 语句等效,会有条件地显示匹配的内容。
只要其中⼀个 th:case 的值为 true就显示标签,则同⼀个 switch 语句中的其他 th:case 属性都将被视为 false即都不显示标签。
示例的Servlet代码:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//往request域对象中存储名为level的数据,测试数据值:level2
req.setAttribute("level","level2");
//调用方法渲染视图
processTemplate("list", req, resp);
}
示例的HTML代码:
list.html
2.9.3. each循环测试switch
银牌会员
金牌会员
白金会员
钻石会员
示例的Servlet代码:
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//往request域对象中存储List集合
req.setAttribute("userList", Arrays.asList("aaa","bbb","ccc","ddd"));
//调用方法渲染视图
processTemplate("each", req, resp);
}
示例的HTML代码:
each.html
用户列表展示页面
| 下标 | 奇数 | 具体值 |
|---|---|---|
| 用户信息为空,没法遍历展示数据 | ||
抽取各个页面的公共部分,类似VUE的组件:
demo操作步骤第一步,创建一个模板文件segment.html ,并在其中创建页面的公共代码片段:
使用 th:fragment来给这个片段命名,如下:
被抽取出来的头部内容
segment.html
segment demo
被抽取出来的头部内容
第二步,在需要的模板页面中进行包含:
使用格式: 代码片段所在页面的逻辑视图 :: 代码片段的名称
| 语法 | 效果 | 特点 |
|---|---|---|
| th:insert | 把目标的代码片段整个插入到当前标签内部 | 它会保留页面自身的标签 |
| th:replace | 用目标的代码替换当前标签 | 它不会保留页面自身的标签 |
| th:include | 把目标的代码片段去除最外层标签,然后再插入到当前标签内部 | 它会去掉片段外层标记,同时保留页面自身标记 |
在需要的模板页面中包含公共代码片段,测试的主要代码如下:
div标签的原始内容div标签的原始内容div标签的原始内容
最后启动Tomcat测试:
查看页面的HTML代码,观察不同语法的差别:
| 语法 | 效果 | 特点 |
|---|---|---|
| th:insert | 把目标的代码片段整个插入到当前标签内部 | 它会保留页面自身的标签 |
| th:replace | 用目标的代码替换当前标签 | 它不会保留页面自身的标签 |
| th:include | 把目标的代码片段去除最外层标签,然后再插入到当前标签内部 | 它会去掉片段外层标记,同时保留页面自身标记 |
- th: insert方法会保留自身的标签, 即体现为页面原来的标签还会保留!!
- th: replace方法不会保留自身的标签, 即体现为页面原来的标签没有被保留!!
- th: include方法会去掉片段的外层标记,同时保留页面自身标记。即体现为页面原来的标签有保留,但是原来声明公共代码片段的外部标签没有被保留!!
第一步,加入jar包:
第二步,在web.xml文件中配置上下文参数属性值,后续servlet需要读取:
view-prefix e/WEB-INF/view/ view-suffix .html
为什么视图模板文件要设置放在WEB-INF目录下?
原因:
位于WEB-INF目录下的文件是不允许浏览器直接访问的,所以我们的视图模板文件放在这个目录下,是一种保护。以免外界可以随意访问视图模板文件。
访问WEB-INF目录下的页面,都必须通过Servlet转发过来,简单说就是:不经过Servlet访问不了, 这样就方便我们在Servlet中检查当前用户是否有权限访问。
当放在WEB-INF目录下之后,里面的文件重定向进不去怎么办?
解决方案: 将请求重定向到Servlet中,再通过Servlet转发到WEB-INF下。
第三步,在src相关目录下创建处理模板文件的Servlet基类:如: src.com.carson.sevlet目录
这个类直接复制粘贴即可,将来使用框架后,这些代码都将被取代。
package com.carson.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;
//重写init()方法(其是servlet的初始化方法)
@Override
public void init() throws ServletException {
//1. 获取当前Servlet的ServletContext对象
ServletContext servletContext = this.getServletContext();
//2. 创建Thymeleaf解析器对象,其接收:一个ServletContext对象
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
//3. 给解析器对象设置参数
//3.1 设置解析的模板模式, HTML是默认模式,明确设置是为了代码更容易理解
templateResolver.setTemplateMode(TemplateMode.HTML);
//3.2 设置前缀
String viewPrefix = servletContext.getInitParameter("view-prefix");
templateResolver.setPrefix(viewPrefix);
//3.3 设置后缀
String viewSuffix = servletContext.getInitParameter("view-suffix");
templateResolver.setSuffix(viewSuffix);
//3.4 设置缓存过期时间(毫秒)
templateResolver.setCacheTTLMs(60000L);
//3.5 设置是否缓存
templateResolver.setCacheable(true);
//3.6 设置服务器端编码方式
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());
}
}
第四步:在Tomcat中配置项目启动路径,这里我设置为:thymeleaf_learn
第五步,在WEB-INF目录下创建index.html文件:
测试ThymeLeaf Demo
Carson初步测试Thymeleaf!
第六步,在servlet目录下创建测试的TestThymeleafServlet,并在web.xml中进行注册
TestThymeleafServlet
package com.carson.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
//类继承处理模板的基础类
public class TestThymeleafServlet extends ViewbaseServlet{
//重写doGet和doPost等方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求域中存储数据
req.setAttribute("username","Welcome to Carson's Target Html!");
req.setAttribute("requestData","这是请求域中存储的数据!");
req.setAttribute("aNotEmptyList", Arrays.asList("aaa","bbb","ccc"));
req.setAttribute("anEmptyList", new ArrayList<>());
//会话域中存储数据
HttpSession session = req.getSession();
session.setAttribute("sessionData","这是会话域中存储的数据!");
//应用域中存储数据
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("applicationData","这是应用域中存储的数据!");
//请求转发跳转到/WEB-INF/view/target.html
//调用父类的processTemplate方法
processTemplate("target",req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
web.xml中
testThymeleafServlet
com.carson.servlet.TestThymeleafServlet
testThymeleafServlet
/testThymeleaf
第七步,在WEB-INF/view目录下编写Thymeleaf模板文件代码:target.html
注: 在编写Thymeleaf的模板文件时,部分代码出现下划线报红?
答: 不用理会,因为模板文件未得到服务器端运行时是会报红的。
target.html
Target Html(目标页面)
未经过服务器解析前的HTML页面
点击跳转
解析携带参数的URL
Thymeleaf表达式的基本内置对象的方法测试:
#lists对象isEmpty方法判断集合整体是否为空aNotEmptyList:测试#lists
#lists对象isEmpty方法判断集合整体是否为空anEmptyList:测试#lists
最后启动Tomcat进行测试:
创作不易!! 欢迎点赞/转发!!!



