当前位置: 首页 > news >正文

详解模板引擎一

详解模板引擎一

  • 引言
  • 一、猜数字游戏案例
    • 1. 约定好前后端交互方式
      • 第一个交互接口
      • 第二个交互接口
    • 2. 如何实现
    • 3. 代码实现
    • 4. 展示结果
      • (1) 抓包结果
      • (2) 浏览器显示结果
    • 5. 分析问题
  • 二、应用模板引擎的第一个案例
    • 1. html 模板文件
    • 2. Servlet 代码
    • 3. 展示结果
      • (1) 抓包结果
      • (2) 浏览器展示结果
      • (3) 前后对比
  • 三、理解应用模板引擎的代码
    • html 模板文件
    • Servlet 代码
      • 1. TemplateEngine 类
      • 2. ServletContextTemplateResolver 类
      • 3. WebContext 类
  • 四、改进猜数字游戏
    • html 模板文件
    • Servlet 代码
    • 抓包结果
  • 五、常用的 Thymeleaf 模板语法
  • 六、th:each 应用案例
    • html 模板文件
    • Servlet 代码
    • 展示结果
      • 浏览器展示结果
      • 抓包结果
    • 分析代码
  • 七、关于 Thymeleaf
    • 准备工作
    • Thymeleaf 的使用流程
    • 总结

引言

在说模板引擎之前,我们先来看看一个猜数字游戏的案例。

一、猜数字游戏案例

1. 约定好前后端交互方式

第一个交互接口

GET 请求:打开 / 刷新页面的时候,可以看到猜数字的基本页面。( 输入框 + 按钮 )
。路径约定为:【 /guess 】

GET 响应:服务器初始化,并在后台生成一个随机数字,响应的内容是呈现出一个 html 页面。( 输入框 + 按钮 )

第二个交互接口

POST 请求:每猜一次数字,就发送一个 POST 请求,同样地,可以看到基本页面。
( 输入框 + 按钮 + 结果 + 次数)。路径约定为:【 /guess 】

POST 响应:服务器处理每一次猜数字的逻辑(猜大了 / 猜小了),响应的内容是呈现出一个 html 页面。( 输入框 + 按钮 + 结果 + 次数)

2. 如何实现

不需要写 ajax 代码,只需要在服务器端实现 Servlet 响应代码即可,之后,在浏览器上输入路径即可显示响应。

(1) 写一个 doGet 方法,处理第一个交互接口。
(2) 写一个 doPost 方法,处理第二个交互接口。

3. 代码实现

@WebServlet("/guess")
public class GuessNumberServlet extends HttpServlet {
    private int toGuess = 0;
    private int count = 0;

    /**
     * 刷新浏览器,获取页面的初始情况,并初始化,生成一个待猜的数字
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");

        // 1. 先生成一个待猜的数字
        Random random = new Random();
        // 生成的数字范围 [0, 100)
        toGuess = random.nextInt(100);
        count = 0;

        // 2. 返回一个页面
        StringBuilder html = new StringBuilder();
        // 约定用 POST 进行提交,这里不能弄错了
        html.append("<form action=\"guess\" method=\"POST\"> ");
        html.append("<input type=\"text\" name=\"num\"> ");
        html.append("<input type=\"submit\" value=\"提交\"> ");
        html.append("</form>");

        resp.getWriter().write(html.toString());
    }

    /**
     * 处理每一次猜的过程,每猜一次数字,就相当于要发送 HTTP 请求一次,所以就像响应一次
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");

        // 1. 从请求中,读取用户提交的数字内容
        String parameter = req.getParameter("num");
        int num = Integer.parseInt(parameter);
        String result = "";

        // 2. 和 toGuess 进行比较
        if (num < toGuess) {
            result = "猜小了";
        } else if (num > toGuess) {
            result = "猜大了";
        } else {
            result = "猜对了";
        }

        // 3. 自增猜的次数
        count++;

        // 4. 构造一个结果页面,能够显示当前猜的结果
        StringBuilder html = new StringBuilder();
        html.append("<form action=\"guess\" method=\"POST\"> ");
        html.append("<input type=\"text\" name=\"num\"> ");
        html.append("<input type=\"submit\" value=\"提交\"> ");
        html.append("</form>");

        // 新增一些显示内容
        html.append("<div>" + result + "</div>");
        html.append("<div> 当前猜的次数: " + count + "次" + "</div>");

        resp.getWriter().write(html.toString());
    }

}

4. 展示结果

(1) 抓包结果

1

2

(2) 浏览器显示结果

2

5. 分析问题

在上面的代码中,可以看到,如果我们需要将响应以 html 页面的形式展示在浏览器 ( 客户端 ) 上,那么,就需要在 Servlet 代码中写上 html 格式的标签。然而,我们只能以字符串拼接的方式来达到 html 这样的页面效果。也就是说,我们没法做到真正的前后端分离。

基于以上的原因,模板引擎就能够有效解决这一问题,所以,模板引擎存在的意义也正是:能够将前后端代码做到前后分离。

二、应用模板引擎的第一个案例

1. html 模板文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>模板引擎</title>
</head>
<body>
    <!-- 我们期望 h3 中的内容经 Servlet 代码中操作之后,能够将 ${message} 替换 -->
    <h3 th:text="${message}"></h3>
</body>
</html>

2. Servlet 代码

@WebServlet("/helloThymeleaf")
public class HelloThymeleafServlet extends HttpServlet {
    // 创建一个模板引擎对象
    private TemplateEngine engine = new TemplateEngine();

    @Override
    public void init() throws ServletException {
        // 完成 Thymeleaf 的初始化操作
        // 初始化操作其实就只做了一件事,告诉模板引擎,它从哪些目录中加载什么样的文件,作为 HTML 模板

        // 创建一个 模板解析器 对象
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
        // 让模板解析器,来加载模板文件
        // 这里需要设置模板文件的前缀后缀,就是告诉模板引擎,需要加载哪些文件到内存中,以备后用
        resolver.setPrefix("/WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("UTF-8");
        // 把解析器对象,给设置到 engine 对象中
        engine.setTemplateResolver(resolver);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");
        // 0. 在执行模板渲染之前,要先进行初始化
        // 模板渲染:把刚才写的模板 html 代码中的 message 变量进行替换
        // 初始化操作需要执行一次即可,放到 init 方法中实现

        // 1. 先从参数中读取用户传过来的 message 值,
        // 这里是 GET 请求,所以我们从 query string 中读取
        String message = req.getParameter("message");

        // 2. 把当前从请求中读取出来的 message 的值和模板中的 ${message} 关联起来
        WebContext webContext = new WebContext(req, resp, this.getServletContext());
        // 这相当于是,以一种键值对的方式,进行替换,将 value 替换成 key ( key : value )
        webContext.setVariable("message", message);

        // 3. 进行最终的渲染 (完成最终替换模板的过程)
        // "demo" 即表示当前模板文件,表示 《demo.html》 去掉后缀的写法
        String html = engine.process("demo", webContext);
        System.out.println(html);
        System.out.println();
        resp.getWriter().write(html);
    }
}

3. 展示结果

(1) 抓包结果

1

(2) 浏览器展示结果

2

(3) 前后对比

3

三、理解应用模板引擎的代码

html 模板文件

<h3 th:text="${message}"></h3>

【 th 】是 Thymeleaf 的缩写,即表示这个属性是由 Thymeleaf 提供的
【 text 】表示这里的类型是字符串,也可以按字面理解为字符串的意思
【 “${message}” 】表示一个具体的变量,其需要在 Java 代码中,将变量值进行替换

Servlet 代码

1. TemplateEngine 类

// 创建一个模板引擎对象
private TemplateEngine engine = new TemplateEngine();

TemplateEngine 是 Thymeleaf 中,最核心的类,功能就是渲染模板。

String html = engine.process("demo", webContext);

最后的一步,通过 process 方法,直接完成模板渲染。

5

2. ServletContextTemplateResolver 类

ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());

resolver.setPrefix("/WEB-INF/template/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");

engine.setTemplateResolver(resolver);

虽然 ServletContextTemplateResolver 类,看起来很长,但它其实很好理解,它存在的意义实际上就是让模板解析器来加载模板文件。怎么告诉它呢?就是让它知道模板文件在哪个路径,包含哪些格式。前缀、后缀这是固定写法。

3. WebContext 类

WebContext webContext = new WebContext(req, resp, this.getServletContext());
webContext.setVariable("message", message);

WebContext 类,我们可以将其理解为一个键值对结构,它存在的主要意义:以一种键值对的方式,进行替换,将 value 替换成 key ( key : value )。

左边的 “message” 相当于 key,右边的 message 相当于 value.
左边的 “message” 就是 【 “${message}” 】,右边的 message 就是我们 Java 代码中的一个变量,我们可以为其赋值。

2

四、改进猜数字游戏

之前,我们实现猜数字游戏的时候,没有做到前后端代码分离,现在,我们尝试使用模板引擎试一下效果。

html 模板文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>猜数字</title>
</head>
<body>
    <form action="guess2" method="POST">
        <input type="text" name="num">
        <input type="submit" value="猜数字">
    </form>

    <!-- th:if 是一个条件显示的逻辑,if 后面的表达式为真就显示,反之,就不显示 -->
    <div th:if="${!newGame}">
        <!-- 猜完游戏的时候,才会显示下面两行代码,第一次加载页面的时候,不显示 -->
        <div th:text="${result}"></div>
        <div th:text="${count}"></div>
    </div>
</body>
</html>

Servlet 代码

@WebServlet("/guess2")
public class GuessNumberServlet2 extends HttpServlet {
    private TemplateEngine engine = new TemplateEngine();

    private int toGuess = 0;
    private int count = 0;

    /**
     * 对模板引擎进行初始化操作
     */
    @Override
    public void init() throws ServletException {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
        resolver.setPrefix("WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("UTF-8");
        engine.setTemplateResolver(resolver);
    }

    /**
     * 刷新浏览器,获取页面的初始情况,并初始化,生成一个待猜的数字
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");

        // 1. 先生成一个待猜的数字
        Random random = new Random();
        // 生成的数字范围 [0, 100)
        toGuess = random.nextInt(100);
        count = 0;

        // 2. 返回一个页面
        // 这里是开启一局新的游戏
        WebContext webContext = new WebContext(req, resp, getServletContext());
        webContext.setVariable("newGame", true);
        engine.process("guessNum", webContext, resp.getWriter());
    }

    /**
     * 处理每一次猜的过程,每猜一次数字,就相当于要发送 HTTP 请求一次,所以就像响应一次
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");

        // 1. 从请求中,读取用户提交的数字内容
        String parameter = req.getParameter("num");
        int num = Integer.parseInt(parameter);
        String result = "";

        // 2. 和 toGuess 进行比较
        if (num < toGuess) {
            result = "猜小了";
        } else if (num > toGuess) {
            result = "猜大了";
        } else {
            result = "猜对了";
        }

        // 3. 自增猜的次数
        count++;

        // 4. 构造一个结果页面,能够显示当前猜的结果
        WebContext webContext = new WebContext(req, resp, getServletContext());
        webContext.setVariable("newGame", false);
        webContext.setVariable("result", result);
        webContext.setVariable("count", count);
        engine.process("guessNum", webContext, resp.getWriter());

    }
}

抓包结果

0

1

五、常用的 Thymeleaf 模板语法

命令功能
th:text在标签体中展示表达式求值结果的文本内容
th:[HTML标签属性]设置任意的 HTML 标签属性的值
th:if当表达式的结果为真时则显示内容,否则不显示
th:each循环访问元素

六、th:each 应用案例

html 模板文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>电话本</title>
</head>
<body>
    <ul>
        <li th:each="person : ${persons}">
            <span th:text="${person.name}"></span>
            <span th:text="${person.phone}"></span>
        </li>
    </ul>
</body>
</html>

Servlet 代码

class Person {
    public String name;
    public String phone;

    public Person(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
}

@WebServlet("/each")
public class EachServlet extends HttpServlet {
    private TemplateEngine engine = new TemplateEngine();

    @Override
    public void init() throws ServletException {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
        resolver.setPrefix("WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("UTF-8");
        engine.setTemplateResolver(resolver);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html; charset = UTF-8");
        List<Person> persons = new ArrayList<>();
        persons.add(new Person("Jack", "123"));
        persons.add(new Person("Rose", "456"));
        persons.add(new Person("Ron", "789"));
        persons.add(new Person("Bruce", "321"));
        persons.add(new Person("Lisa", "654"));

        WebContext webContext = new WebContext(req, resp, this.getServletContext());
        webContext.setVariable("persons", persons);

        engine.process("each", webContext, resp.getWriter());
    }
}

展示结果

浏览器展示结果

1

抓包结果

HTTP 响应的正文 body,是经过模板渲染后的结果:

2

分析代码

这个代码的意思很简单,我们创建一个 Person 类,之后通过其构造函数,new 一些对象,并放入顺序表中,最后,以 html 的格式,展现在浏览器上。

1

当我们没有通过网络编程,只是经过 Java 单机代码写一个程序的时候,我们可以通过 for each 这样的增强循环,来遍历整个顺序表。

然而,当我们使用网络编程的时候,需要考虑到前后端交互,那么,服务器端的代码,不仅要考虑逻辑,也要考虑到客户端如何接收到一个明显的响应。

七、关于 Thymeleaf

需要明确:Java 中的模板引擎有很多种,Thymeleaf 只是其中一个模板引擎,而由于这是 Spring 官方推荐的,所以我们先使用 Thymeleaf,对之后学习 Spring 框架,也能够做到很好的连接了。

使用模板 Thymeleaf 这样的模板引擎,主要就是模板渲染这一步骤,它就像是为图片上色一样,这就很像美术课上,我们先得将一个物体的轮廓勾勒出来,再为其添上颜色。

如果没上过美术课的小伙伴,也没有关系,答题卡大家一定都用过,考试时,老师给大家发的空白答题卡就相当于一个模板文件,当我们涂空白答题卡的时候,就是在渲染模板。

准备工作

1. 先从 maven 仓库,引入依赖

1

2. 创建好工作目录

webapp / WEB-INF / template / 【模板文件…】

2

Thymeleaf 的使用流程

一、编写 html 模板文件,放到指定目录中

二、编写Servlet 代码

  1. 初始化一个 TemplateEngine 实例
  2. 创建一个 ServletContextTemplateResolver 实例,此外,指定需要加载的模板文件的路径、格式、字符集
  3. 创建一个 WebContext 实例,把模板中的变量和 Java 中的变量关联起来,也就是把被替换的变量和新变量关联起来。
  4. 使用 TemplateEngine 类 提供的 process 方法完成最终的模板渲染

总结

相对于 Java 其他的模板引擎,Thymeleaf 较为复杂。然而,我们还是得熟知它的用法。虽然刚开始学的时候,很多类不熟悉,但是多敲几遍代码,就可以很顺利地做出来。因为 Thymeleaf 和 JDBC 编程 一样,是一个固定的流程。

虽然,在上面的几个案例中,未将 html 分离的写法 和 使用模板引擎的写法,这两者看起来代码长度差不了多少,但如果是上百行 html 代码呢?难道我们都需要使用转义字符拼接吗?所以,模板引擎存在的最大意义,就是让 【 后端的 Java 代码 】 和 【 前端的 html, css, JS 代码 】做出分离。

此外,说来说去,其实并不是使用 Thymeleaf 相应的 API 麻烦。而是,当我们进行 Web 开发的时候,这本身就是一件麻烦的事情。

因为,当我们写一个 Java 的 “单机代码” 的时候,只需要程序员自己看着,就明白了逻辑。但是 Web 开发,就需要涉及到客户端与服务器之间的交互,最终的结果,我们需要将数据转化成一个较为简单的主观内容,展现给那些不懂代码的普通用户。那么,如何进行转化,如何进行处理请求并做出什么样的响应,这些问题,本身就是一个较为麻烦的事情。

这就好像:如果我们一个人在家里吃饭的时候,可以大口大口、毫无顾虑地吃饭,但在公共场合的时候,需要考虑到别人的感受,所以我们自己就要注意形象了。

相关文章:

  • 大数据框架介绍与实操
  • springboot网上课程教学授课网站java
  • springboot+vue+elementui二手手机销售商城网站
  • JVM虚拟机栈的五道面试题
  • qemu与gdb内核调试环境搭建
  • STM32CUBEMX开发GD32F303(13)----定时器TIM捕获PWM测量频率与占空比
  • Spark Structured Streaming - 1
  • 自动化测试中的验证码问题
  • 工具箱目录
  • 数学知识——阶乘分解
  • 第二篇 如何选择操作系统
  • java二叉排序树(含java代码详解)
  • java计算机毕业设计图书管理系统演示录像 源码+系统+数据库+lw文档+mybatis+运行部署
  • 我的世界MinecraftJava版开服教程(Linux)开服器开服包下载开服网站服务器开服核心开服端开服软件mac版Java启动器
  • 业绩全线飘红,两翼业务增长强劲,东软教育能否乘势而上?
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 10个确保微服务与容器安全的最佳实践
  • Angular6错误 Service: No provider for Renderer2
  • bearychat的java client
  • Computed property XXX was assigned to but it has no setter
  • E-HPC支持多队列管理和自动伸缩
  • JDK 6和JDK 7中的substring()方法
  • js 实现textarea输入字数提示
  • Linux后台研发超实用命令总结
  • MQ框架的比较
  • NSTimer学习笔记
  • Vue2 SSR 的优化之旅
  • webpack入门学习手记(二)
  • XML已死 ?
  • 关于extract.autodesk.io的一些说明
  • 基于HAProxy的高性能缓存服务器nuster
  • 两列自适应布局方案整理
  • 前嗅ForeSpider采集配置界面介绍
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 如何胜任知名企业的商业数据分析师?
  • 如何选择开源的机器学习框架?
  • 写给高年级小学生看的《Bash 指南》
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 一文看透浏览器架构
  • FaaS 的简单实践
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • # .NET Framework中使用命名管道进行进程间通信
  • #Linux(make工具和makefile文件以及makefile语法)
  • #pragma 指令
  • #微信小程序:微信小程序常见的配置传旨
  • $.ajax,axios,fetch三种ajax请求的区别
  • $refs 、$nextTic、动态组件、name的使用
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (安卓)跳转应用市场APP详情页的方式
  • (二)构建dubbo分布式平台-平台功能导图
  • (小白学Java)Java简介和基本配置
  • (转)Mysql的优化设置
  • (转)视频码率,帧率和分辨率的联系与区别
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选