• Thymeleaf简要学习笔记(转自代码重工)


    转载至:http://heavy_code_industry.gitee.io/code_heavy_industry/

    一、Thymeleaf简介

    1.1 Thymeleaf的同行

    ​ JSP、Freemarker、Velocity等等,它们有一个共同的名字:服务器端模板技术

    1.2 Thymeleaf优势

    • SpringBoot官方推荐使用的视图模板技术,和SpringBoot完美整合。
    • 不经过服务器运算仍然可以直接查看原始值,对前端工程师更友好

    1.3 thymeleaf和vue的关系

    Thymeleaf和vue不是一类事务。

    • 模板引擎:Thymeleaf、freemarker、JSP。
    • 前端框架:vue、angularjs、react。

    ​ Thymeleaf是一个替代JSP的模板引擎。使用Thymeleaf或其他模板的时候也可以使用前端框架。现在github上很多工程就是springboot+Thymeleaf+vue三者结合的

    1.4 物理视图和逻辑视图

    1. 物理视图

      在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

      所以,路径开头的部分我们称之为视图前缀,路径结尾的部分我们称之为视图后缀

    2. 逻辑视图

      物理视图=视图前缀+逻辑视图+视图后缀,上面的例子中:

      在这里插入图片描述

    二、在服务器端引入Thymeleaf环境

    2.1 加入jar包

    2.2 配置上下文参数

    物理视图=视图前缀+逻辑视图+视图后缀

    
    <context-param>
        <param-name>view-prefixparam-name>
        <param-value>/WEB-INF/view/param-value>
    context-param>
    <context-param>
        <param-name>view-suffixparam-name>
        <param-value>.htmlparam-value>
    context-param>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    说明:param-value中设置的前缀、后缀的值不是必须叫这个名字,可以根据实际情况和需求进行修改

    为什么要放在WEB-INF目录下?

    原因:WEB-INF目录不允许浏览器直接访问,所以我们的视图模板文件放在这个目录下,是一种保护。以免外界可以随意访问视图模板文件

    访问WEB-INF目录下的页面,都必须通过Servlet转发过来,简单说就是:不经过Servlet访问不了。

    这样就方便我们在Servlet中检查当前用户是否有权限访问。

    那放在WEB-INF目录下之后,重定向进不去怎么办?

    重定向到Servlet,再通过Servlet转发到WEB-INF下。

    2.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());
        }
    }
    
    • 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
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    2.4 HelloWorld

    <servlet>
        <servlet-name>TestThymeleafServletservlet-name>
        <servlet-class>com.atguigu.thymeleaf.servlet.TestThymeleafServletservlet-class>
    servlet>
    <servlet-mapping>
        <servlet-name>TestThymeleafServletservlet-name>
        <url-pattern>/TestThymeleafServleturl-pattern>
    servlet-mapping>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、基本语法:th名称空间

    在这里插入图片描述

    四、基本语法:表达式语法

    4.1 修改标签文本值

    <p th:text="标签体新值">标签体原始值p>
    
    • 1
    • th:text作用

      • 不经过服务器解析,直接用浏览器打开HTML文件,看到的是『标签体原始值』
      • 经过服务器解析,Thymeleaf引擎根据th:text属性指定的『标签体新值』去替换『标签体原始值』
    • 字面量

      『字面量』是一个经常会遇到的概念,我们可以对照『变量』来理解它的含义。

      • 变量:变量名字符串本身不是它的值,它指向的才是它的值
      • 字面量:它就是字面上的含义,我们从『字面』上看到的直接就是它的值

      现在我们在th:text属性中使用的就是『字面量』,它不指代任何其他值

    4.2 修改指定属性值

    <input type="text" name="username" th:value="文本框新值" value="文本框旧值" />
    
    • 1

    语法:任何HTML标签原有的属性,前面加上『th:』就都可以通过Thymeleaf来设定新值。

    4.3 解析URL地址

    • 基本语法

      <p th:text="@{/aaa/bbb/ccc}">标签体原始值p>
      
      • 1

      经过解析后得到:

      /view/aaa/bbb/ccc

      所以@{}的作用是在字符串前附加『上下文路径』

      这个语法的好处是:实际开发过程中,项目在不同环境部署时,Web应用的名字有可能发生变化。所以上下文路径不能写死。而通过@{}动态获取上下文路径后,不管怎么变都不怕啦

    • 首页使用URL地址解析

      在这里插入图片描述

      如果我们直接访问index.html本身,那么index.html是不需要通过Servlet,当然也不经过模板引擎,所以index.html上的Thymeleaf的任何表达式都不会被解析。

      解决办法:通过Servlet访问index.html,这样就可以让模板引擎渲染页面了:

    进一步的好处:

    通过上面的例子我们看到,所有和业务功能相关的请求都能够确保它们通过Servlet来处理,这样就方便我们统一对这些请求进行特定规则的限定。

    4.4 直接执行表达式

    Servlet代码:

    request.setAttribute("reqAttrName", "hello-value");
    
    • 1

    1页面代码:

    <p>有转义效果:[[${reqAttrName}]]p>
    <p>无转义效果:[(${reqAttrName})]p>
    
    • 1
    • 2

    执行效果:

        <p>有转义效果:<span>hello-value</span>p>
        <p>无转义效果:<span>hello-valuespan>p>
    
    • 1
    • 2

    五、 基本语法:访问域对象

    5.1 域对象

    • 请求域

      在请求转发的场景下,我们可以借助HttpServletRequest对象内部给我们提供的存储空间,帮助我们携带数据,把数据发送给转发的目标资源。

      请求域:HttpServletRequest对象内部给我们提供的存储空间

    • 会话域应用域

      PS:在我们使用的视图是JSP的时候,域对象有4个

      • pageContext
      • request:请求域
      • session:会话域
      • application:应用域

      所以在JSP的使用背景下,我们可以说域对象有4个,现在使用Thymeleaf了,没有pageContext

    5.2 在Servlet中将数据存入属性域

    • 操作请求域

      Servlet中代码:

      String requestAttrName = "helloRequestAttr";
      String requestAttrValue = "helloRequestAttr-VALUE";
      request.setAttribute(requestAttrName, requestAttrValue);
      
      • 1
      • 2
      • 3

      Thymeleaf表达式:

      <p th:text="${helloRequestAttr}">request field valuep>
      
      • 1
    • 操作绘画域

      Servlet中代码:

      // ①通过request对象获取session对象
      HttpSession session = request.getSession();
      // ②存入数据
      session.setAttribute("helloSessionAttr", "helloSessionAttr-VALUE");
      
      • 1
      • 2
      • 3
      • 4

      Thymeleaf表达式:

      <p th:text="${session.helloSessionAttr}">这里显示会话域数据p>
      
      • 1
    • 操作应用域

      Servlet中代码:

      // ①通过调用父类的方法获取ServletContext对象
      ServletContext servletContext = getServletContext();
      // ②存入数据
      servletContext.setAttribute("helloAppAttr", "helloAppAttr-VALUE");
      
      • 1
      • 2
      • 3
      • 4

      Thymeleaf表达式:

      <p th:text="${application.helloAppAttr}">这里显示应用域数据p>
      
      • 1

    六、基本语法:获取请求参数

    具体来说,我们这里探讨的是在页面上(模板页面)获取请求参数。底层机制是:

    在这里插入图片描述

    6.1 一个名字一个值

    <p th:text="${param.username}">这里替换为请求参数的值p>
    
    • 1

    6.2 一个名字多个值

    <p th:text="${param.team}">这里替换为请求参数的值p>
    
    • 1

    如果想要精确获取某一个值,可以使用数组下标。页面代码:

    <p th:text="${param.team[0]}">这里替换为请求参数的值p>
    <p th:text="${param.team[1]}">这里替换为请求参数的值p>
    
    • 1
    • 2

    七、基本语法:内置对象

    所谓内置对象其实就是在表达式中可以直接使用的对象

    用法举例:

    <h3>表达式的基本内置对象h3>
    <p th:text="${#request.getClass().getName()}">这里显示#request对象的全类名p>
    <p th:text="${#request.getContextPath()}">调用#request对象的getContextPath()方法p>
    <p th:text="${#request.getAttribute('helloRequestAttr')}">调用#request对象的getAttribute()方法,读取属性域p>
    
    • 1
    • 2
    • 3
    • 4

    基本思路:

    • 如果不清楚这个对象有哪些方法可以使用,那么就通过getClass().getName()获取全类名,再回到Java环境查看这个对象有哪些方法
    • 内置对象的方法可以直接调用
    • 调用方法时需要传参的也可以直接传入参

    7.1 公共内置对象

    Servlet中将List集合数据存入请求域:

    request.setAttribute("aNotEmptyList", Arrays.asList("aaa","bbb","ccc"));
    request.setAttribute("anEmptyList", new ArrayList<>());
    
    • 1
    • 2

    页面代码:

    <p>#list对象isEmpty方法判断集合整体是否为空aNotEmptyList:<span th:text="${#lists.isEmpty(aNotEmptyList)}">测试#listsspan>p>
    <p>#list对象isEmpty方法判断集合整体是否为空anEmptyList:<span th:text="${#lists.isEmpty(anEmptyList)}">测试#listsspan>p>
    
    • 1
    • 2

    八、基本语法:${}中的表达式本质是OGNL

    8.1 OGNL

    OGNL:Object-Graph Navigation Language对象-图 导航语言

    8.2 对象图

    从根对象触发,通过特定的语法,逐层访问对象的各种属性。

    在这里插入图片描述

    8.3 OGNL语法

    • 起点

    在Thymeleaf环境下,${}中的表达式可以从下列元素开始:

    • 访问属性域的起点
      • 请求域属性名
      • session
      • application
    • param
    • 内置对象
      • request
      • session
      • lists
      • strings
    • 属性访问语法
    • 访问对象属性:使用getXxx()、setXxx()方法定义的属性
      • 对象.属性名
    • 访问List集合或数组
      • 集合或数组[下标]
    • 访问Map集合
      • Map集合.key
      • Map集合[‘key’]

    九、基本语法:分支与迭代

    9.1 分支

    • if和unless

    让标记了th:if、th:unless的标签根据条件决定是否显示。

    示例的实体类:

    public class Employee {
    
        private Integer empId;
        private String empName;
        private Double empSalary;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例的Servlet代码:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
        // 1.创建ArrayList对象并填充
        List<Employee> employeeList = new ArrayList<>();
    
        employeeList.add(new Employee(1, "tom", 500.00));
        employeeList.add(new Employee(2, "jerry", 600.00));
        employeeList.add(new Employee(3, "harry", 700.00));
    
        // 2.将集合数据存入请求域
        request.setAttribute("employeeList", employeeList);
    
        // 3.调用父类方法渲染视图
        super.processTemplate("list", request, response);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    示例的HTML代码:

    <table>
        <tr>
            <th>员工编号th>
            <th>员工姓名th>
            <th>员工工资th>
        tr>
        <tr th:if="${#lists.isEmpty(employeeList)}">
            <td colspan="3">抱歉!没有查询到你搜索的数据!td>
        tr>
        <tr th:if="${not #lists.isEmpty(employeeList)}">
            <td colspan="3">有数据!td>
        tr>
        <tr th:unless="${#lists.isEmpty(employeeList)}">
            <td colspan="3">有数据!td>
        tr>
    table>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    if配合not关键词和unless配合原表达式效果是一样的,看自己的喜好。

    • switch
    <h3>测试switchh3>
    <div th:switch="${user.memberLevel}">
        <p th:case="level-1">银牌会员p>
        <p th:case="level-2">金牌会员p>
        <p th:case="level-3">白金会员p>
        <p th:case="level-4">钻石会员p>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    9.2 迭代

    <h3>测试eachh3>
    <table>
        <thead>
            <tr>
                <th>员工编号th>
                <th>员工姓名th>
                <th>员工工资th>
            tr>
        thead>
        <tbody th:if="${#lists.isEmpty(employeeList)}">
            <tr>
                <td colspan="3">抱歉!没有查询到你搜索的数据!td>
            tr>
        tbody>
        <tbody th:if="${not #lists.isEmpty(employeeList)}">
            
            <tr th:each="employee : ${employeeList}">
                <td th:text="${employee.empId}">empIdtd>
                <td th:text="${employee.empName}">empNametd>
                <td th:text="${employee.empSalary}">empSalarytd>
            tr>
        tbody>
    table>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在迭代过程中,可以参考下面的说明使用迭代状态:

    <h3>测试eachh3>
    <table>
        <thead>
            <tr>
                <th>员工编号th>
                <th>员工姓名th>
                <th>员工工资th>
                <th>迭代状态th>
            tr>
        thead>
        <tbody th:if="${#lists.isEmpty(employeeList)}">
            <tr>
                <td colspan="3">抱歉!没有查询到你搜索的数据!td>
            tr>
        tbody>
        <tbody th:if="${not #lists.isEmpty(employeeList)}">
            
            <tr th:each="employee,empStatus : ${employeeList}">
                <td th:text="${employee.empId}">empIdtd>
                <td th:text="${employee.empName}">empNametd>
                <td th:text="${employee.empSalary}">empSalarytd>
                <td th:text="${empStatus.count}">counttd>
            tr>
        tbody>
    table>
    
    • 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

    十、基本语法:包含其他模板文件

    10.1 应用场景

    抽取各个页面的公共部分

    在这里插入图片描述

    10.2 创建页面的代码片段

    使用th:fragment来给这个片段命名:

    <div th:fragment="header">
        <p>被抽取出来的头部内容p>
    div>
    
    • 1
    • 2
    • 3

    10.3 包含到有需要的页面

    语法效果
    th:insert把目标的代码片段整个插入到当前标签内部
    th:replace用目标的代码替换当前标签
    th:include把目标的代码片段去除最外层标签,然后再插入到当前标签内部

    页面代码举例:

    
    <div id="badBoy" th:insert="segment :: header">
        div标签的原始内容
    div>
    
    <div id="worseBoy" th:replace="segment :: header">
        div标签的原始内容
    div>
    
    <div id="worstBoy" th:include="segment :: header">
        div标签的原始内容
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    猿创征文|在CSDN学习的那些事
    电力感知边缘计算网关产品设计方案-电力采集
    C#,怎么修改(VS)Visual Studio 2022支持的C#版本
    sklearnex 让你的 sklearn 机器学习模型训练快得飞起?
    mac连不上5gwifi怎么解决 macbook无法连接5g wifi
    leetcode刷题记录
    llinux的更目录下的文件作用和举例
    深度学习笔记——神经网络(ANN)搭建过程+python代码
    J. 金色传说【10.14训练补题】
    SQL中为什么不要使用1=1
  • 原文地址:https://blog.csdn.net/weixin_42200347/article/details/126315017