day08 Thmeleaf

day08 Thmeleaf

1. 内容讲解

1.1 MVC
1.1.1 为什么需要MVC

曾经编写过下面这段代码
在这里插入图片描述

这段代码虽然说可以实现在登录失败之后跳转回到登录页面,并且展示失败信息,但是代码实在是太恶心了,根本没法维护,所以我们需要将视图展示抽取出来,单独作为一个View视图层

但是我们如果只使用HTML作为视图的话,它是无法展示动态数据的,所以我们对HTML的新的期待:既能够正常显示页面,又能在页面中包含动态数据部分。而动态数据单靠HTML本身是无法做到的,所以此时我们需要引入服务器端动态视图模板技术。

1.1.2 MVC概念

M:Model模型

V:View视图

C:Controller控制器

MVC是在表述层开发中运用的一种设计理念。主张把封装数据的『模型』显示用户界面的『视图』、**协调调度的『控制器』**分开。

好处:

  • 进一步实现各个组件之间的解耦
  • 让各个组件可以单独维护
  • 将视图分离出来以后,我们后端工程师和前端工程师的对接更方便
1.1.3 MVC和三层架构之间关系

在这里插入图片描述

1.2 Thymeleaf的简介
1.2.1 Thymeleaf的概念

Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP,Velocity,FreeMaker等, 它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。它的主要作用是在静态页面上渲染显示动态数据

1.2.2 Thymeleaf的优势
  • 22@
<!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>
1.2.3 物理视图和逻辑视图
① 物理视图

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

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

上面的例子中:

视图前缀逻辑视图视图后缀物理视图
/pages/user/login.html/pages/user/login.html
/pages/user/login_success.html/pages/user/login_success.html
2.1 Thymeleaf的HelloWorld
2.1.1 加入jar包

在这里插入图片描述

2.1.2 配置上下文参数

在这里插入图片描述

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

<!--    配置前缀-->
    <context-param>
        <param-name>start</param-name>
        <param-value>/pages/</param-value>
    </context-param>
<!--    配置后缀-->
    <context-param>
        <param-name>end</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.1.3 创建Servlet基类

这个类大家直接复制粘贴即可,将来使用框架后,这些代码都将被取代。

package com.atguigu.servlet.base;

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("start");

        templateResolver.setPrefix(viewPrefix);

        // ③设置后缀
        String viewSuffix = servletContext.getInitParameter("end");

        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());
    }
}
2.1.4 入门案例代码
① 创建index.html文件

在这里插入图片描述

② index.html编写超链接访问Servlet
<h2 th:text="${message}"></h2>
③ 修改Servlet让其继承ViewBaseServlet

在这里插入图片描述

④ 在doPost()方法中跳转到Thymeleaf页面

package com.atguigu.servlet.app;

import com.atguigu.servlet.base.ViewBaseServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/index.html")
public class IndexServlet extends ViewBaseServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setAttribute("message", "helloWorld");
        // 请求转发跳转到/WEB-INF/view/index.html
        processTemplate("index", req, resp);
    }
}

⑤ Thymeleaf页面代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
<h1>首页 index</h1>
<h2 th:text="${message}"></h2>

</body>
</html>

3. Thymeleaf的基本语法

3.1 th名称空间

在这里插入图片描述

3.2 表达式语法
3.2.1 修改标签文本值

代码示例:

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

代码示例:

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

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

3.2.3 解析URL地址

代码示例:

<!--
使用Thymeleaf解析url地址
-->
<a th:href="@{/index.html}">访问index.html</a>

经过解析后得到:

/webday08/index.html

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

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

① 首页使用URL地址解析

在这里插入图片描述

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

解决办法:通过Servlet访问index.html,这样就可以让模板引擎渲染页面了:
在这里插入图片描述

进一步的好处:

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

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

参照官方文档说明:
在这里插入图片描述

<a th:href="@{/hello(uname='李四',indexMsg=${indexMsg},hobby='read')}">HelloServlet4</a>
3.3 域对象在Thymeleaf中的使用
3.3.1 回顾域对象

域对象是在服务器中有一定作用域范围的对象,在这个范围内的所有动态资源都能够共享域对象中保存的数据

3.3.2 回顾域对象的类型
① 请求域

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

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

在这里插入图片描述

② 会话域(后面学)

会话域的范围是一次会话
在这里插入图片描述

③ 应用域(后面学)

应用域的范围是整个项目全局
在这里插入图片描述

3.3.3 在Thymeleaf中操作域对象

我们通常的做法是,在Servlet中将数据存储到域对象中,而在使用了Thymeleaf的前端页面中取出域对象中的数据并展示

① 操作请求域

Servlet中代码:

req.setAttribute("msg", "独钓寒江雪");
// /pages/ hello.html

processTemplate("hello", req, resp);

Thymeleaf表达式:

<h1 th:text="${msg}">Hello</h1>
② 操作会话域(后期讲)
③ 操作应用域(后期讲)
3.4 获取请求参数
3.4.1 获取请求参数的语法
${param.参数名}
3.4.2 根据一个参数名获取一个参数值

页面代码:

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

<!--<h3>获取请求参数</h3>-->
<p th:text="${param.uname}"></p>
<p th:text="${param.pwd}"></p>
<p th:text="${param.hobby}"></p>
<p th:text="${param.hobby[0]}"></p>
<p th:text="${param.hobby[1]}"></p>

页面显示效果:

在这里插入图片描述

3.4.3 根据一个参数名获取多个参数值

页面代码:

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

页面显示效果:
在这里插入图片描述

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

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

页面显示效果:
在这里插入图片描述

3.5 内置对象
3.5.1 内置对象的概念

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

3.5.2 基本内置对象

在这里插入图片描述

用法举例:

<h3>表达式的基本内置对象</h3>
<p th:text="${#request.getContextPath()}">调用#request对象的getContextPath()方法</p>
<p th:text="${#request.getAttribute('helloRequestAttr')}">调用#request对象的getAttribute()方法,读取属性域</p>

<h3>内置对象(Thymeleaf内可以直接使用的对象)</h3>
<p th:text="${#request.getServletContext()}"></p>
<p th:text="${#request.getContextPath()}"></p>
<p th:text="${#request.getAttribute('msg')}"></p>

基本思路:

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

在这里插入图片描述

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

req.setAttribute("msg", "独钓寒江雪");
ArrayList<String > list = new ArrayList<>();
list.add("D");
list.add("B");
list.add("A");

req.setAttribute("myList", list);

// 获取请求参数
Map<String, String[]> map = req.getParameterMap();
map.forEach((k, v) -> System.out.println(k + "--->" + Arrays.toString(v)));
processTemplate("hello", req, resp);

页面代码:

<h3>公共内置对象(等价于Java中的工具类Math)</h3>
<p th:text="${#strings.isEmpty(msg)}"></p>
<p th:text="${#strings.indexOf(msg, '')}"></p>
<p th:text="${#lists.isEmpty(myList)}"></p>
<p th:text="${#lists.size(myList)}"></p>
<p th:text="${#lists.sort(myList)}"></p>

公共内置对象对应的源码位置:
在这里插入图片描述

3.6 OGNL
3.6.1 OGNL的概念

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

3.6.2 对象图的概念

从根对象触发,通过特定的语法,逐层访问对象的各种属性。
在这里插入图片描述

3.6.3 OGNL语法
① 起点

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

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

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

示例的实体类:

package com.atguigu.pojo;

public class Person {
    private String name;
    private int age;
    private char gender;
    private Computer computer;

    public Person() {
    }

    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public Person(String name, int age, char gender, Computer computer) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.computer = computer;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNum(){
        return 20;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }

    public Computer getComputer() {
        return computer;
    }

    public void setComputer(Computer computer) {
        this.computer = computer;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", computer=" + computer +
                '}';
    }
}

示例的Servlet代码:

package com.atguigu.servlet.app;

import com.atguigu.pojo.Person;
import com.atguigu.servlet.base.ViewBaseServlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

@WebServlet("/flow")
public class FowServlet extends ViewBaseServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        req.setAttribute("hero", "虞姬");
        req.setAttribute("num", 4);

        ArrayList<String > list1 = new ArrayList<>();
        Collections.addAll(list1, "安其拉", "米莱迪", "李白");
        req.setAttribute("list1", list1);

        ArrayList<Person> ps = new ArrayList<>();

        Collections.addAll(ps, new Person("李白", 20, '男'),
                new Person("杜甫", 22, '男'),
                new Person("王安石", 23, '男'));

        req.setAttribute("pList", ps);

        processTemplate("flow", req, resp);
    }
}

示例的HTML代码:

<div th:if="${#strings.isEmpty(hero)}">
    字符串为空
</div>

<div th:unless="${#strings.isEmpty(hero)}">
    字符串不为空
</div>

<div th:if="${not #strings.isEmpty(hero)}">
    字符串为空
</div>
<hr>

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

② switch
<h3>测试switch</h3>
<div th:switch="${num}">
    <div th:case="1">A</div>
    <div th:case="2">B</div>
    <div th:case="3">C</div>
    <div th:case="4">D</div>
</div>
3.7.2 迭代

在迭代过程中,可以参考下面的说明使用迭代状态:
在这里插入图片描述

<table border="1" width="600" style="text-align: center">
    <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
    </tr>
    <tr th:each="p:${pList}">
        <td th:text="${p.name}"></td>
        <td th:text="${p.age}"></td>
        <td th:text="${p.gender}"></td>
    </tr>
</table>
<hr>
<table border="1" width="900" style="text-align: center">
    <tr>
        <th>下标</th>
        <th>第几个</th>
        <th>总数量</th>
        <th>当前元素</th>
        <th>是否是第一个元素</th>
        <th>是否是最后一个元素</th>
        <th>是否为偶数个</th>
        <th>是否为奇数个</th>
        <th>姓名</th>
    </tr>

    <tr th:each="e,s:${pList}">
       <td th:text="${s.index}"></td>
       <td th:text="${s.count}"></td>
       <td th:text="${s.size}"></td>
       <td th:text="${s.current}"></td>
       <td th:text="${s.first}"></td>
       <td th:text="${s.last}"></td>
       <td th:text="${s.even}"></td>
       <td th:text="${s.odd}"></td>
       <td th:text="${e.name}"></td>
    </tr>
</table>

3.8 Thymeleaf包含其他模板文件
3.8.1 应用场景

抽取各个页面的公共部分:
在这里插入图片描述

3.8.2 操作步骤
① 创建页面的公共代码片段

使用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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值