商城开发全流程之Thymeleaf
in JavaDevelop with 0 comment

商城开发全流程之Thymeleaf

in JavaDevelop with 0 comment

1 Thymeleaf

1.1 Thymeleaf的概念

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

1.2 Thymeleaf的优势

<!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.3 物理视图和逻辑视图

1.3.1 物理视图

在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是物理视图
在这里插入图片描述
如果我们把所有的HTML页面都放在某个统一的目录下,那么转发地址就会呈现出明显的规律:

/pages/user/login.html
/pages/user/login_success.html
/pages/user/regist.html
/pages/user/regist_success.html

……

路径的开头都是:/pages/user/

路径的结尾都是:.html

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

1.3.2 逻辑视图

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

上面的例子中:

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

1.4 Thymeleaf 入门案例

1.4.1 导入jar包

在这里插入图片描述

1.4.2 配置上下文参数

在这里插入图片描述

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

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

1.4.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.4.4 创建index.html 页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>系统首页</title>
    <base href="http://localhost:8090/07_thymeleaf/">
</head>
<body>
    <a href="helloServlet">跳转到hello.html页面</a>
</body>
</html>

1.4.5 创建HelloServlet

  1. 编辑Servlet
package com.auguigu.servlet;

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

public class HelloServlet extends ViewBaseServlet{

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("已经访问了Serlvet了");
        request.setAttribute("msg","我是服务器数据!!!!!!!!");
        this.processTemplate("hello",request,response);
    }
}


  1. 编辑web.xml配置
 <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.auguigu.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/helloServlet</url-pattern>
    </servlet-mapping>

1.4.6 编辑Thymeleaf页面

<!DOCTYPE html>

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>这是一个欢迎页面</title>
</head>
<body>
    <h1 th:text="${msg}"></h1>
</body>
</html>

1.5 Thymeleaf基本语法

1.5.1 th名称空间

在这里插入图片描述

1.5.2 th:text 作用

代码示例:

<p th:text="标签体新值">标签体原始值</p>

1.5.3 修改指定属性值

代码示例:

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

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

1.5.4 解析URL地址

代码示例:

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

经过解析后得到:

/webday08/index.html

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

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

1.5.5 关于index.html访问说明

  1. 为什么在index.html 使用th标签失效
    在这里插入图片描述
  2. 原理说明
    在这里插入图片描述
    如果我们直接访问index.html本身,那么index.html是不需要通过Servlet,当然也不经过模板引擎,所以index.html上的Thymeleaf的任何表达式都不会被解析。

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

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

  1. 完成IndexServlet
public class IndexServlet extends ViewBaseServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        doPost(request,response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //跳转到index.html页面中
        System.out.println("这里是idnexServlet的位置");
        request.setAttribute("msg","欢迎页面信息");
        this.processTemplate("index",request,response);
    }
}
  1. 配置servlet路径
<servlet>
        <servlet-name>IndexServlet</servlet-name>
        <servlet-class>com.auguigu.servlet.IndexServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>IndexServlet</servlet-name>
        <url-pattern>/index.html</url-pattern>
    </servlet-mapping>

1.6 关于域的说明

1.6.1 请求域(Request域)

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

请求域:HttpServletRequest对象内部给我们提供的存储空间
总结: 在同一个请求中实现数据共享
在这里插入图片描述

1.6.2 会话域(Session域)

说明: 在同一个会话下,可以发起多个交互, 在同一个会话下实现数据共享.
在这里插入图片描述

1.6.3 应用域(Application域)

说明: 在同一个tocmat服务器中实现数据共享
在这里插入图片描述

1.6.4 Thymeleaf中操作域对象

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

  1. 编辑Servlet将数据保存到与对象中
@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1.向request域中添加数据
        request.setAttribute("requestMsg","request域中的数据");
        //2.向Session域中添加数据
        request.getSession().setAttribute("sessionMsg","Session域中的数据");
        //3.向Application域中添加数据
        request.getServletContext().setAttribute("contextMsg","ConText域中的数据");

        this.processTemplate("value",request,response);
    }
  1. 从域中获取数据
 	<!--1.从request域中获取数据-->
    <h1 th:text="${requestMsg}"></h1>

    <!--2.从Session域中获取数据-->
    <h1 th:text="${session.sessionMsg}"></h1>

    <!--3.从Application域中获取数据-->
    <h1 th:text="${application.contextMsg}"></h1>

在这里插入图片描述

1.7 获取请求参数

1.7.1 获取请求参数的语法

	${param.参数名}

1.7.2 页面效果展现

  1. 编辑页面
	<!--thymeleaf参数获取测试 -->
    <a href="paramServlet?id=1&name=tomcat">参数取值操作</a>
  1. 页面参数获取
 <h1>获取参数信息</h1>
    <p th:text="${param.id}"></p>
    <p th:text="${param.name}"></p>

1.7.3 同名参数提交说明

  1. 编辑页面
<!--thymeleaf参数获取测试 -->
 <a href="paramServlet?id=1&name=tomcat&hobby=java&hobby=mysql&hobby=js">参数取值操作</a>

  1. 页面取值
 <!--获取同名参数 获取数组,按照数组类型输出 -->
    <p th:text="${param.hobby}"></p>
    <p th:text="${param.hobby[0]}"></p>
    <p th:text="${param.hobby[1]}"></p>
    <p th:text="${param.hobby[2]}"></p>

在这里插入图片描述

1.8 内置对象

1.8.1 内置对象的概念

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

1.8.2 基本内置对象

在这里插入图片描述
用法举例:

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

基本思路:

1.8.3 公共内置对象

在这里插入图片描述
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>

1.9 OGNL表达式

1.9.1 OGNL的概念

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

1.9.2 对象图的概念

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

1.9.3 OGNL语法

① 起点

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

② 属性访问语法

1.10 分支与迭代

1.10.1 if和unless

让标记了th:if、th:unless的标签根据条件决定是否显示。
语法说明:
当th:if判断为真时,显示标签体的内容. 否则不显示, 与unless的恰好相反.
if配合not关键词和unless配合原表达式效果是一样的,看自己的喜好。

  1. 编辑Servlet
	@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("num",1);
        this.processTemplate("ifOrFor",request,response);
    }
  1. 编辑页面html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>分支语法</title>
</head>
<body>
    <h1>分支语法结构</h1>
    <!--当判断结果为真时 显示标签体内容-->
    <p th:text="${num}">num为100</p>
    <p th:if="${num > 5}">num为100 >5</p>
    <p th:if="${not(num > 5)}">num > 5 not</p>
    <p th:unless="${num > 5}"> num > 5 unless</p>


</body>
</html>

1.10.2 switch

语法:

  1. th:switch
  2. th:case
@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("num",1);
        request.setAttribute("level",3);
        this.processTemplate("ifOrFor",request,response);
    }
123456
 	<h3>switch分支语法</h3>
    <div th:switch="${level}">
        <p th:case="1">一级</p>
        <p th:case="2">二级</p>
        <p th:case="3">三级</p>
    </div>

1.10.3 迭代用法

语法:

		<tr th:each="teacher,status : ${teacherList}">
            <td th:text="${status.count}">这里显示编号</td>
            <td th:text="${teacher.teacherName}">这里显示老师的名字</td>
        </tr>

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

<!--遍历显示请求域中的teacherList-->
<table border="1" cellspacing="0" width="500">
    <tr>
        <th>编号</th>
        <th>姓名</th>
    </tr>
    <tbody th:if="${#lists.isEmpty(teacherList)}">
        <tr>
            <td colspan="2">教师的集合是空的!!!</td>
        </tr>
    </tbody>

    <!--
集合不为空,遍历展示数据
-->
    <tbody th:unless="${#lists.isEmpty(teacherList)}">
        <!--
使用th:each遍历
用法:
1. th:each写在什么标签上? 每次遍历出来一条数据就要添加一个什么标签,那么th:each就写在这个标签上
2. th:each的语法    th:each="遍历出来的数据,数据的状态 : 要遍历的数据"
3. status表示遍历的状态,它包含如下属性:
 index 遍历出来的每一个元素的下标
 count 遍历出来的每一个元素的计数
-->
        <tr th:each="teacher,status : ${teacherList}">
            <td th:text="${status.count}">这里显示编号</td>
            <td th:text="${teacher.teacherName}">这里显示老师的名字</td>
        </tr>
    </tbody>
</table>

1.11 Thymeleaf包含其他模板文件

1.11.1 应用场景

说明: 由于头部标签每个页面都会使用,如果重复编辑则耦合性高,则需要将页面信息进行抽取,之后统一进行引用.
在这里插入图片描述

1.11.2 编辑头部页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>头部</title>
</head>
<body>
    <div th:fragment="jd_head">
      我是京东页面头部
    </div>
</body>
</html>

1.11.2 编辑页面

语法 效果 特点
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.12 综合项目练习

1.12.1 完成列表展现

  1. 初始化页面
    在这里插入图片描述
    2.列表展现页面
    在这里插入图片描述

1.12.2 用户新增页面

说明: 点击提交按钮时,完成数据提交,并且重定向到列表页面
在这里插入图片描述

1.12.3 用户修改操作

说明: 当点击修改按钮时,实现数据回显, 默认密码不显示,如果赋值默认修改.
在这里插入图片描述

1.12.4 完成用户删除操作

提示: 根据ID删除数据 可以实现数据的动态传递. 具体代码参数文档资料

	 <td>
                <a th:href="@{/toUpdateUser(id=${user.id})}"><button>编辑</button></a>
                <a th:href="@{/deleteUserById(id=${user.id})}"><button>删除</button></a>
     </td>