JavaWeb:(八)Thymeleaf

8.1 Thymeleaf简介

8.1.1 Thymeleaf的同行

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

8.1.2 Thymeleaf优势

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

8.1.3 物理视图和逻辑视图

  1. 物理视图

    在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是物理视图

    image-20220628003025632

    /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. 逻辑视图

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

    上面的例子中:

    视图前缀逻辑视图视图后缀物理视图
    /pages/user/login.html/pages/user/login.html
    /pages/user/login_success.html/pages/user/login_success.html

8.2 在服务器端引入Thymeleaf环境

  1. 加入jar包

image-20220628004218044

  1. 配置上下文参数

    image-20220628004301466

    image-20220628004757589

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

    <!-- 在上下文参数中配置视图前缀和视图后缀 -->
    <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下。

  2. 创建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. HelloWorld

    • 创建index.html

      image-20220628005422387

    • 在index.html编写超链接

      <a href="/view/TestThymeleafServlet">初步测试Thymeleaf</a>
      
    • 创建Servlet

      <servlet>
          <servlet-name>TestThymeleafServlet</servlet-name>
          <servlet-class>com.atguigu.thymeleaf.servlet.TestThymeleafServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>TestThymeleafServlet</servlet-name>
          <url-pattern>/TestThymeleafServlet</url-pattern>
      </servlet-mapping>
      
    • 修改Servlet继承的父类

      image-20220628005547948

    • 在doGet()方法中跳转到Thymeleaf页面

      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          
          // 1.声明当前请求要前往的视图名称
          String viewName = "target";
          
          // 2.调用ViewBaseServlet父类中的解析视图模板的方法
          super.processTemplate(viewName, request, response);
          
      }
      
    • Thymeleaf页面(target.htm)

      <!DOCTYPE html>
      
      <!-- 在html标签内加入Thymeleaf名称空间的声明 -->
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      
          <!-- 在p标签的基础上,使用Thymeleaf的表达式,解析了一个URL地址 -->
          <p th:text="@{'/aaa/bbb/ccc'}">Thymeleaf将在这里显示一个解析出来的URL地址</p>
      
      </body>
      </html>
      

8.3 基本语法:th名称空间

images

8.4 基本语法:表达式语法

8.4.1 修改标签文本值

代码示例:

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

8.4.2 修改指定属性值

代码示例:

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

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

8.4.3 解析URL地址

  1. 基本语法

    代码示例:

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

    经过解析后得到:

    /view/aaa/bbb/ccc

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

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

  2. 使用URL地址解析

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

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

    ./images

    进一步的好处:

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

  3. 给URL地址后面附加请求参数

    • 带参数的URL表达式:/fruit.do?id=1&&name=桃子
    • Thymeleaf表达式:@{/fruit.do(id=1,name=‘桃子’)}

8.4.4 直接执行表达式

Servlet代码:

request.setAttribute("reqAttrName", "<span>hello-value</span>");

页面代码:

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

执行效果:

    <p>有转义效果:&lt;span&gt;hello-value&lt;/span&gt;</p>
    <p>无转义效果:<span>hello-value</span></p>

8.5 基本语法:访问域对象

8.5.1 域对象

在Thymeleaf中,有三个域对象

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

在JSP中,有四个域对象

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

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

  1. 操作请求域

    Servlet中代码:

    String requestAttrName = "helloRequestAttr";
    String requestAttrValue = "helloRequestAttr-VALUE";
    
    request.setAttribute(requestAttrName, requestAttrValue);
    

    Thymeleaf表达式:

    <p th:text="${helloRequestAttr}">request field value</p>
    
  2. 操作会话域

    Servlet中代码:

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

    Thymeleaf表达式:

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

    Servlet中代码:

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

    Thymeleaf表达式:

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

8.6 基本语法:获取请求参数

8.6.1 一个名字一个值

页面代码:

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

页面显示效果:

image-20220628014105288

8.6.2 一个名字多个值

页面代码:

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

页面显示效果:

image-20220628014551411

8.7 基本语法:内置对象

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

8.7.1 基本内置对象

./images

用法举例:

<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>

基本思路:

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

8.7.2 公共内置对象

./images

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

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

页面代码:

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

公共内置对象对应的源码位置:

./images

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

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

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

OGNL语法

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

  • 访问属性域的起点
    • 请求域属性名
    • session
    • application
  • param
  • 内置对象
    • #request
    • #session
    • #lists
    • #strings

属性访问语法

  • 访问对象属性:使用getXxx()、setXxx()方法定义的属性
    • 对象.属性名
  • 访问List集合或数组
    • 集合或数组[下标]
  • 访问Map集合
    • Map集合.key
    • Map集合[‘key’]

8.9 分之与迭代

8.9.1 分支

  1. if和unless

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

    示例的实体类:

    public class Employee {
    
        private Integer empId;
        private String empName;
        private Double empSalary;
        ...
    }
    

    示例的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);
    }
    

    示例的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>
    

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

  2. switch

    <h3>测试switch</h3>
    <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>
    

8.9.2 迭代

<h3>测试each</h3>
<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}">empId</td>
            <td th:text="${employee.empName}">empName</td>
            <td th:text="${employee.empSalary}">empSalary</td>
        </tr>
    </tbody>
</table>

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

./images

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

应用场景:抽取各个页面的公共部分。

代码实例

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

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

包含到有需要的页面

语法效果
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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值