• Thymeleaf学习笔记


     Thymeleaf官网: https://www.thymeleaf.org/index.html

    Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎。

    Thymeleaf的主要目标是为您的开发工作流程带来优雅自然的模板 -HTML可以在浏览器中正确显示,并且可以作为静态原型工作,从而可以在开发团队中加强协作。

    Thymeleaf拥有用于Spring Framework的模块,与您喜欢的工具的大量集成以及插入您自己的功能的能力,对于现代HTML5 JVM Web开发而言,Thymeleaf是理想的选择-尽管它还有很多工作要做。

    Spring官方支持的服务的渲染模板中,并不包含jsp。而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVC的视图技术,及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,你只用关注Thymeleaf的语法即可。

    特点

    • 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。

    • 开箱即用:它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。

    • 多方言支持:Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。

    • 与SpringBoot完美整合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器,我们可以像以前操作jsp一样来操作Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。

     

    环境准备

    创建module

    这里使用spring 脚手架创建:

     

     

     

    pom.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <parent>
     6         <groupId>org.springframework.boot</groupId>
     7         <artifactId>spring-boot-starter-parent</artifactId>
     8         <version>2.2.4.RELEASE</version>
     9         <relativePath/>
    10     </parent>
    11     <groupId>com.yangw.test</groupId>
    12     <artifactId>thymeleaf-demo</artifactId>
    13     <version>1.0.0-SNAPSHOT</version>
    14     <packaging>jar</packaging>
    15 
    16     <name>thymeleaf-demo</name>
    17     <description>Demo project for Spring Boot</description>
    18 
    19     <properties>
    20         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    21         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    22         <java.version>1.8</java.version>
    23     </properties>
    24 
    25     <dependencies>
    26         <dependency>
    27             <groupId>org.springframework.boot</groupId>
    28             <artifactId>spring-boot-starter-thymeleaf</artifactId>
    29         </dependency>
    30         <dependency>
    31             <groupId>org.springframework.boot</groupId>
    32             <artifactId>spring-boot-starter-web</artifactId>
    33         </dependency>
    34 
    35         <dependency>
    36             <groupId>org.springframework.boot</groupId>
    37             <artifactId>spring-boot-starter-test</artifactId>
    38             <scope>test</scope>
    39         </dependency>
    40     </dependencies>
    41 
    42     <build>
    43         <plugins>
    44             <plugin>
    45                 <groupId>org.springframework.boot</groupId>
    46                 <artifactId>spring-boot-maven-plugin</artifactId>
    47             </plugin>
    48         </plugins>
    49     </build>
    50 
    51 </project>
    View Code

    默认配置

    不需要做任何配置,启动器已经帮我们把Thymeleaf的视图器配置完成:

     而且,还配置了模板文件(html)的位置,与jsp类似的前缀+ 视图名 + 后缀风格:

    • 默认前缀:classpath:/templates/

    • 默认后缀:.html

    所以如果我们返回视图:user,会指向到 classpath:/templates/user.html

    Thymeleaf默认会开启页面缓存,提高页面并发能力。但会导致我们修改页面不会立即被展现,因此我们关闭缓存:

    # 关闭Thymeleaf的缓存
    spring.thymeleaf.cache=false

    ******另外,修改完毕页面,需要使用快捷键Ctrl + Shift + F9来刷新工程******

    快速开始

    我们准备一个controller,控制视图跳转:

     在resources/templates/下新建一个html模板:

     注意,把html 的名称空间,改成:xmlns:th="http://www.thymeleaf.org" 会有语法提示.

    启动SpringBoot应用,浏览器访问!

     

    语法

    Thymeleaf的主要作用是把model中的数据渲染到html中,因此其语法主要是如何解析model中的数据.

    Thymeleaf通过${}来获取model中的变量,注意这不是el表达式,而是ognl表达式,但是语法非常像;

    我们的表达式写在一个名为:th:text 的标签属性中,这个叫做指令.

    动静结合

    Thymeleaf崇尚自然模板,意思就是模板是纯正的html代码,脱离模板引擎,在纯静态环境也可以直接运行。现在如果我们直接在html中编写 ${}这样的表达式,显然在静态环境下就会出错,这不符合Thymeleaf的理念。

    Thymeleaf中所有的表达式都需要写在指令中,指令是HTML5中的自定义属性,在Thymeleaf中所有指令都是以th:开头。因为表达式${user.name}是写在自定义属性中,因此在静态环境下,表达式的内容会被当做是普通字符串,浏览器会自动忽略这些指令,这样就不会报错了!

    现在,我们不经过SpringMVC,而是直接用浏览器打开页面看看:

     

    • 静态页面中,th指令不被识别,但是浏览器也不会报错,把它当做一个普通属性处理。这样span的默认值请登录就会展现在页面

    • 如果是在Thymeleaf环境下,th指令就会被识别和解析,而th:text的含义就是替换所在标签中的文本内容,于是msg的值就替代了 h1中默认的thymeleaf

    指令的设计,正是Thymeleaf的高明之处,也是它优于其它模板引擎的原因。动静结合的设计,使得无论是前端开发人员还是后端开发人员可以完美契合。

    向下兼容:

    但是要注意,如果浏览器不支持Html5怎么办?

    如果不支持这种th:的命名空间写法,那么可以把th:text换成 data-th-text,Thymeleaf也可以兼容.

     

    另外,th:text指令出于安全考虑,会把表达式读取到的值进行处理,防止html的注入。

    例如,<p>你好</p>将会被格式化输出为$lt;p$gt;你好$lt;/p$lt;

    如果想要不进行格式化输出,而是要输出原始内容,则使用th:utext来代替.

     th:text   th:utext

    ${对象.属性}  和  ${对象[‘属性’]}  都支持

    因此,Thymeleaf提供了自定义变量来解决:

    <h2 th:object="${user}">
       <p>Name: <span th:text="*{name}">Jack</span>.</p>
       <p>Age: <span th:text="*{age}">21</span>.</p>
       <p>friend: <span th:text="*{friend.name}">Rose</span>.</p>
    </h2>
    • 首先在 h2上 用 th:object="${user}"获取user的值,并且保存

    • 然后,在h2内部的任意元素上,可以通过 *{属性名}的方式,来获取user中的属性,这样就省去了大量的user.前缀了

    Thymeleaf中提供了一些内置对象,并且在这些对象中提供了一些方法,方便我们来调用。获取这些对象,需要使用 #对象名来引用

    一些环境相关对象

    对象作用
    #ctx 获取Thymeleaf自己的Context对象
    #requset 如果是web程序,可以获取HttpServletRequest对象
    #response 如果是web程序,可以获取HttpServletReponse对象
    #session 如果是web程序,可以获取HttpSession对象
    #servletContext 如果是web程序,可以获取HttpServletContext对象
     

    Thymeleaf提供的全局对象:

    #dates 处理java.util.date的工具对象
    #calendars 处理java.util.calendar的工具对象
    #numbers 用来对数字格式化的方法
    #strings 用来处理字符串的方法
    #bools 用来判断布尔值的方法
    #arrays 用来护理数组的方法
    #lists 用来处理List集合的方法
    #sets 用来处理set集合的方法
    #maps 用来处理map集合的方法

    举例:

     

     

     字面值

    有的时候,我们需要在指令中填写基本类型如:字符串、数值、布尔等,并不希望被Thymeleaf解析为变量,这个时候称为字面值

    • 字符串字面值

      使用一对'引用的内容就是字符串字面值了:

    • 数字字面值

      数字不需要任何特殊语法, 写的什么就是什么,而且可以直接进行算术运算

    • 布尔字面值

      布尔类型的字面值是true或false:

     

     拼接

    我们经常会用到普通字符串与表达式拼接的情况:

    <span th:text="'欢迎您:' + ${user.name} + '!'"></span>

    字符串字面值需要用'',拼接起来非常麻烦,Thymeleaf对此进行了简化,使用一对|即可:

    <span th:text="|欢迎您:${user.name}|"></span>

    与上面是完全等效的,这样就省去了字符串字面值的书写。

     运算

    需要注意:${}内部的是通过OGNL表达式引擎解析的,外部的才是通过Thymeleaf的引擎解析,因此运算符尽量放在${}外进行。

    • 算术运算

      支持的算术运算符:+ - * / %

      <span th:text="${user.age}"></span>
      <span th:text="${user.age}%2 == 0"></span>

    • 比较运算

      支持的比较运算:>, <, >= and <= ,但是>, <不能直接使用,因为xml会解析为标签,要使用别名。

      注意 == and !=不仅可以比较数值,类似于equals的功能。

      可以使用的别名:gt (>), lt (<), ge (>=), le (<=), not (!). Also eq (==), neq/ne (!=).

    • 条件运算

      • 三元运算


      <span th:text="${user.sex} ? '男':'女'"></span>

      三元运算符的三个部分:conditon ? then : else


      condition:条件

      then:条件成立的结果

      else:不成立的结果

      其中的每一个部分都可以是Thymeleaf中的任意表达式。

      • 默认值

        有的时候,我们取一个值可能为空,这个时候需要做非空判断,可以使用 表达式 ?: 默认值简写:


      <span th:text="${user.name} ?: '二狗'"></span>

      当前面的表达式值为null时,就会使用后面的默认值。

      注意:?:之间没有空格。

     

    循环

    循环也是非常频繁使用的需求,我们使用th:each指令来完成:

    假如有用户的集合:users在Context中。


    <tr th:each="user : ${users}">
       <td th:text="${user.name}">Onions</td>
       <td th:text="${user.age}">2.41</td>
    </tr>
    • ${users} 是要遍历的集合,可以是以下类型:

      • Iterable,实现了Iterable接口的类

      • Enumeration,枚举

      • Interator,迭代器

      • Map,遍历得到的是Map.Entry

      • Array,数组及其它一切符合数组结果的对象

    在迭代的同时,我们也可以获取迭代的状态对象:


    <tr th:each="user,stat : ${users}">
       <td th:text="${user.name}">Onions</td>
       <td th:text="${user.age}">2.41</td>
    </tr>

    stat对象包含以下属性:

    • index,从0开始的角标

    • count,元素的个数,从1开始

    • size,总元素个数

    • current,当前遍历到的元素

    • even/odd,返回是否为奇偶,boolean值

    • first/last,返回是否为第一或最后,boolean值

    逻辑判断

    有了if和else,我们能实现一切功能^_^。

    Thymeleaf中使用th:if 或者 th:unless ,两者的意思恰好相反。


    <span th:if="${user.age} < 24">小鲜肉</span>

    如果表达式的值为true,则标签会渲染到页面,否则不进行渲染。

    以下情况被认定为true:

    • 表达式值为true

    • 表达式值为非0数值

    • 表达式值为非0字符

    • 表达式值为字符串,但不是"false","no","off"

    • 表达式不是布尔、字符串、数字、字符中的任何一种

    其它情况包括null都被认定为false

    分支控制switch

    这里要使用两个指令:th:switchth:case


    <div th:switch="${user.role}">
     <p th:case="'admin'">用户是管理员</p>
     <p th:case="'manager'">用户是经理</p>
     <p th:case="*">用户是别的玩意</p>
    </div>

    需要注意的是,一旦有一个th:case成立,其它的则不再判断。与java中的switch是一样的。

    另外th:case="*"表示默认,放最后。

    JS模板

    模板引擎不仅可以渲染html,也可以对JS中的进行预处理。而且为了在纯静态环境下可以运行,其Thymeleaf代码可以被注释起来:


    <script th:inline="javascript">
       const user = /*[[${user}]]*/ {};
       const age = /*[[${user.age}]]*/ 20;
       console.log(user);
       console.log(age)
    </script>
    • 在script标签中通过th:inline="javascript"来声明这是要特殊处理的js脚本

    • 语法结构:


      const user = /*[[Thymeleaf表达式]]*/ "静态环境下的默认值";

      因为Thymeleaf被注释起来,因此即便是静态环境下, js代码也不会报错,而是采用表达式后面跟着的默认值。

     

    页面静态化介绍

    问题分析

    页面是通过Thymeleaf模板引擎渲染后返回到客户端。在后台需要大量的数据查询,而后渲染得到HTML页面。会对数据库造成压力,并且请求的响应时间过长,并发能力不高。

    大家能想到什么办法来解决这个问题?

    首先我们能想到的就是缓存技术,比如之前学习过的Redis不过Redis适合数据规模比较小的情况。假如数据量比较大,例如我们的商品详情页。每个页面如果10kb,100万商品,就是10GB空间,对内存占用非常大。此时就给缓存系统带来极大压力,如果缓存崩溃,接下来倒霉的就是数据库了。

    所以缓存并不是万能的,某些场景需要其它技术来解决,比如静态化

    什么是静态化

    静态化是指把动态生成的HTML页面变为静态内容保存,以后用户的请求到来,直接访问静态页面,不再经过服务的渲染。

    静态的HTML页面可以部署在nginx中,从而大大提高并发能力,减小tomcat压力。

    如何实现静态化

    目前,静态化页面都是通过模板引擎来生成,而后保存到nginx服务器来部署。常用的模板引擎比如:

    • Freemarker

    • Velocity

    • Thymeleaf

    我们之前就使用的Thymeleaf,来渲染html返回给用户。Thymeleaf除了可以把渲染结果写入Response,也可以写到本地文件,从而实现静态化

    Thymeleaf实现静态化

    概念

    • Context:运行上下文

    • TemplateResolver:模板解析器

    • TemplateEngine:模板引擎

    Context 上下文:

      用来保存模型数据,当模板引擎渲染时,可以从Context上下文中获取数据用于渲染。

           当与SpringBoot结合使用时,我们放入Model的数据就会被处理到Context,作为模板渲染的数据使用。

    TemplateResolver 模板解析器:

      用来读取模板相关的配置,例如:模板存放的位置信息,模板文件名称,模板文件的类型等等。

           当与SpringBoot结合时,TemplateResolver已经由其创建完成,并且各种配置也都有默认值,比如模板存放位置,其默认值就是:templates。比如模板文件类型,其默认值就是html。

    TemplateEngine 模板引擎:

      用来解析模板的引擎,需要使用到上下文、模板解析器。分别从两者中获取模板中需要的数据,模板文件。然后利用内置的语法规则解析,从而输出解析后的文件。来看下模板引擎进行处理的函数:

      
    templateEngine.process("模板名", context, writer);

    三个参数:

    • 模板名称

    • 上下文:里面包含模型数据

    • writer:输出目的地的流

    在输出时,我们可以指定输出的目的地,如果目的地是Response的流,那就是网络响应。如果目的地是本地文件,那就实现静态化了。

    而在SpringBoot中已经自动配置了模板引擎,因此我们不需要关心这个。现在我们做静态化,就是把输出的目的地改成本地文件即可!

    具体实现

     Service代码

     1 package com.leyou.web.service;
     2 
     3 import com.leyou.web.utils.ThreadUtils;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.stereotype.Service;
     6 import org.thymeleaf.TemplateEngine;
     7 import org.thymeleaf.context.Context;
     8 
     9 import java.io.File;
    10 import java.io.PrintWriter;
    11 
    12 @Service
    13 public class GoodsHtmlService {
    14     @Autowired
    15     private GoodsService goodsService;
    16     @Autowired
    17     private TemplateEngine templateEngine;  //Thymeleaf的模板引擎
    18     /**
    19      * 生成静态的HTML模板页面,并保存到Nginx的路径
    20      * @param spuId
    21      */
    22     public void createHtml(Long spuId){
    23         PrintWriter writer = null;
    24         try {
    25             //1.调用goodsServer方法将加载的Map数据模型设置到Context(Thymeleaf)中
    26             Context context = new Context();
    27             context.setVariables(this.goodsService.loadData(spuId));
    28             //2.指定文件流(nginx服务器下的路径)
    29             File file = new File("D:\develop\nginx-1.16.1\html\item\"+spuId+".html");
    30             writer = new PrintWriter(file);
    31             //3.执行静态化方法
    32             templateEngine.process("item",context,writer);
    33         } catch (Exception e) {
    34             e.printStackTrace();
    35         } finally {
    36             if (writer != null)
    37                 writer.close();
    38         }
    39     }
    40 
    41     /**
    42      * 新建线程处理页面静态化
    43      * @param spuId
    44      */
    45     public void asyncExcute(Long spuId) {
    46         ThreadUtils.execute(()->createHtml(spuId));
    47         /*ThreadUtils.execute(new Runnable() {
    48             @Override
    49             public void run() {
    50                 createHtml(spuId);
    51             }
    52         });*/
    53     }
    54 
    55 
    56 }
    View Code
    线程工具类:
     1 package com.leyou.web.utils;
     2 
     3 import java.util.concurrent.ExecutorService;
     4 import java.util.concurrent.Executors;
     5 
     6 public class ThreadUtils {
     7 
     8     //多线程
     9     private static final ExecutorService es = Executors.newFixedThreadPool(10);
    10 
    11     public static void execute(Runnable runnable) {
    12         es.submit(runnable);
    13     }
    14 }
    View Code

     

    什么时候创建静态文件

    我们编写好了创建静态文件的service,那么问题来了:什么时候去调用它呢?

    想想这样的场景:

    假如大部分的商品都有了静态页面。那么用户的请求都会被nginx拦截下来,根本不会到达我们的leyou-goods-web服务。只有那些还没有页面的请求,才可能会到达这里。

    因此,如果请求到达了这里,我们除了返回页面视图外,还应该创建一个静态页面,那么下次就不会再来麻烦我们了。

    所以,我们在GoodsController中添加逻辑,去生成静态html文件:

     1 package com.leyou.web.controller;
     2 
     3 import com.leyou.web.service.GoodsHtmlService;
     4 import com.leyou.web.service.GoodsService;
     5 import org.springframework.beans.factory.annotation.Autowired;
     6 import org.springframework.stereotype.Controller;
     7 import org.springframework.ui.Model;
     8 import org.springframework.web.bind.annotation.GetMapping;
     9 import org.springframework.web.bind.annotation.PathVariable;
    10 
    11 import java.util.Map;
    12 
    13 @Controller
    14 public class GoodsController {
    15 
    16     @Autowired
    17     private GoodsHtmlService goodsHtmlService;
    18     @Autowired
    19     private GoodsService goodsService;
    20     @GetMapping("item/{id}.html")
    21     public String toItemPage(@PathVariable("id") Long id, Model model){
    22 
    23         Map<String,Object> result = goodsService.loadData(id);
    24         model.addAllAttributes(result);
    25         this.goodsHtmlService.asyncExcute(id);  //生成静态化页面
    26         return "item";  //返回视图名称,这里是 thymeleaf的 templates/item.html
    27 
    28     }
    29 }
    View Code

    注意:生成html 的代码不能对用户请求产生影响,所以这里我们使用额外的线程进行异步创建。

     另外,在实际业务中当我们的数据发生变化的时候,我们要对静态化页面作相应的更新操作...

    nginx代理静态页面

    接下来,我们修改nginx,让它对商品请求进行监听,指向本地静态页面,如果本地没找到,才进行反向代理:

     

     

    首次访问的速度:

    nginx服务器生成的相应的html文件

    第2次开始就直接从nginx返回了,故时间特别的短...

     

    thymeleaf中加载静态资源的方式  @{***} 的方式

  • 相关阅读:
    司马光 王安石
    辛弃疾
    伯仲叔季
    三国时代
    西汉 东汉 三国(曹魏 蜀汉 东吴)
    数量关系练习题
    为什么不推荐使用外键约束
    Google Map API申请
    Android传感器——加速度传感器
    第三届空间信息智能服务研讨会
  • 原文地址:https://www.cnblogs.com/xin1006/p/12286195.html
Copyright © 2020-2023  润新知