模板引擎 - Thymeleaf

    • 1. 模板引擎简介
    • 2. 模板引擎与 Thymeleaf 关系
    • 3. 模板引擎原理
    • 4. 使用流程
        • 流程一:通过 maven 引入依赖
        • 流程二:创建 HTML 模板文件
        • 流程三:编写 servlet 代码
        • 流程四:部署程序
    • 5. 理解只创建一个引擎实例?
        • (1) ServletContext
        • 代码示例:多个 servlet 共享数据
        • (2) 监听器
        • (3)修改 Thymeleaf 引擎初始化代码
    • 6. Thymeleaf 模板语法
        • (1)常用命令及功能
        • (2)设置标签文本
        • (3)设置标签属性
        • (4)条件判断
        • (5)循环

1. 模板引擎简介

模板引擎?你可能第一次听说模板引擎,估计你会禁不住想问:什么是模板引擎呢?

模板引擎(这里特指用于Web开发的模板引擎),是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的html文档;

  • 从字面上理解:模板引擎最重要的就是 模板 二字,相当于做好一个模板后并套入对应位置的数据,最终以html的格式展示出来;
  • 将模板设计好之后直接填充数据即可,而不需要重新设计整个页面,这样就提高了页面以及代码的复用性;

2. 模板引擎与 Thymeleaf 关系

Java中模板引擎有许多,模板引擎作为动态网页发展进步的产物,在最初并且流传度最广的jsp就是一个模板引擎,但由于jsp的缺点比较多,所以很多人弃用jsp选用第三方的模板引擎,市面上开源的第三方的模板引擎也比较多,有Thymeleaf、FreeMaker、Velocity等模板引擎受众较广。

使用 Thymeleaf 优点:

  • 动静分离:Thymeleaf 使用 html 通过一些特定标签语法代表其含义,而且并未破坏html结构,即使无网络、不通过后端渲染也能在浏览器成功打开,大大方便了界面的测试和修改;

关系:Thymeleaf 是模板引擎的其中一种产品!!!

3. 模板引擎原理

  • 首先:客户端浏览器发起HTTP请求到 servlet中;
  • servlet 调用模板引擎组织 模板(静态不动的资源)+数据(动态的资源),将两者拼接为一个HTML字符串;
  • 将该HTML字符串返回给服务端;

4. 使用流程

流程一:通过 maven 引入依赖

  • maven 中央仓库搜索 Thymeleaf 并选择一个版本;

maven 中央仓库网址:https://mvnrepository/

进入之后,搜索 Thymeleaf:

  • 选择 3.0.12版本;

  • 拷贝依赖代码到 pox.xml 中并刷新;


同时需要使用servlet,因此也要引入servlet依赖包;

 <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

流程二:创建 HTML 模板文件

创建 hello.html , 放到 webapp/WEB-INF/templates 目录中;

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
   <h3>网页模板技术学习</h3>
   <p th:text="${message}"></p>
</body>
</html>

流程三:编写 servlet 代码

操作步骤

  • 创建一个模板引擎;
  • 创建一个网页模板解析器;
  • 设置渲染时编码;
  • 设置网页模板文件路径的前缀与后缀;
  • 将模板解析器绑定到模板引擎中;
  • 创建一个web上下文(环境的语义,里面是map结构,存放键值对数据);
  • 设置键值对的数据;
  • 返回渲染后的网页字符串;
package org.example;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

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

@WebServlet("/hello")

public class HelloServlet extends HttpServlet {
    //模板引擎都是返回HTML,因此重写HTML
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置body格式与编码格式
        resp.setContentType("text/html; charset = utf-8");

        //1.创建一个模板引擎
        TemplateEngine engine = new TemplateEngine();

        //2.创建一个网页模板解析器
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());

        //3.设置渲染时编码
        resolver.setCharacterEncoding("utf-8");

        //4.设置网页模板文件路径的前缀与后缀
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");

        //5.将模板解析器绑定到模板引擎中
        engine.setTemplateResolver(resolver);

        //6.创建一个web上下文(环境的语义,里面是map结构,可以存放键值对数据)
        WebContext webContext =new WebContext(req,resp,getServletContext());
        //7.设置键值对的数据:可以理解为:为网页模板定义了一个变量(变量名为message,值为hello模板引擎)
        webContext.setVariable("message","hello模板引擎");

        //模板引擎渲染网页模板:第一个参数为模板名称,第二个参数为web上下文(里面保存了数据)
        //会根据模板解析器设置的前缀+模板名称+后缀,为模板路径,查找到模板,再组织模板内容+数据
        //8.返回值就是渲染后的网页字符串
        String html =engine.process("hello",webContext);
        resp.getWriter().write(html);

    }
}

流程四:部署程序

通过 URL

http://127.0.0.1:8080/Thymeleaf-study/hello

访问服务器,可以看到网页显示:

此时,页面看起来仍是静态不变的,做如下更改:

 //此处为固定页面显示
  //webContext.setVariable("message","hello模板引擎");
   //设置动态变化的 msg=xxx
   webContext.setVariable("message", req.getParameter("msg"));

这样就可以通过浏览器地址栏URL输入内容的不同而产生不同的效果!

代码分析

但以上 servlet 代码,执行效率并不高,且存在冗余!
原因:每次只要发送URL请求:就会创建一个模板引擎及解释器;

优化代码

将创建的模板引擎与解释器放置在init()方法中,该方法只调用一次!

package org.example;



import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

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

@WebServlet("/hello")


public class HelloServlet extends HttpServlet {

    //1.创建一个模板引擎
    TemplateEngine engine = new TemplateEngine();
    @Override
    public void init() throws ServletException {

        //2.创建一个网页模板解析器
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());

        //3.设置渲染时编码
        resolver.setCharacterEncoding("utf-8");

        //4.设置网页模板文件路径的前缀与后缀
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");

        //5.将模板解析器绑定到模板引擎中
        engine.setTemplateResolver(resolver);

    }

    //模板引擎都是返回HTML,因此重写HTML
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置body格式与编码格式
        resp.setContentType("text/html; charset = utf-8");

        //创建一个web上下文(环境的语义,里面是map结构,可以存放键值对数据)
        WebContext webContext =new WebContext(req,resp,getServletContext());
        //设置键值对的数据:可以理解为:为网页模板定义了一个变量(变量名为message,值为hello模板引擎)
        //此处为固定页面显示
        //webContext.setVariable("message","hello模板引擎");
        //设置动态变化的 msg=xxx
        webContext.setVariable("message", req.getParameter("msg"));

        //模板引擎渲染网页模板:第一个参数为模板名称,第二个参数为web上下文(里面保存了数据)
        //会根据模板解析器设置的前缀+模板名称+后缀,为模板路径,查找到模板,再组织模板内容+数据
        //返回值就是渲染后的网页字符串
        String html =engine.process("hello",webContext);
        resp.getWriter().write(html);

    }
}

5. 理解只创建一个引擎实例?

上面优化后,还存在另一个问题

当创建另一个 Servlet 时,还是需要创建一个 TemplateEngine 实例并进行初始化,因此,还是存在多个模板引擎对象与解析器对象的,但其实是完全没有必要的!!!

一个完整的项目中, 只需要创建一个 TemplateEngine, 并且只初始化一次即可~

为了达到这样的目的, 就需要使用 Servlet 中的 ServletContext 和 “监听器”;

(1) ServletContext

ServletContext 是一个 Servlet 程序中全局储存信息的空间, 服务器开始就存在, 服务器关闭才销毁;

  • Tomcat 在启动时,它会为每个Web app都创建一个对应的 ServletContext;
  • 一个Web应用中的所有 Servlet 共享同一个 ServletContext 对象;
  • 可以通过 HttpServlet.getServletContext() 或者HttpServletRequest.getServletContext() 获取到当前 webappServletContext 对象;

如下所示关系


ServletContext 对象的重要方法:

方法功能
void setAttribute(String name,Object obj)设置属性(键值对的形式)
Object getAttribute(String name)根据属性名获取属性值, 如果 name 不存在, 返回 null
void removeAttribute(String name)删除对应的属性

代码示例:多个 servlet 共享数据

(1) 创建一个 writeServlet类;

package org.example;

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

@WebServlet("/write")
public class writeServlet extends HttpServlet {
    // write?data=xxx
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String data = req.getParameter("data");
        //将数据写入servlet共享的上下文环境
        //获取当前servletContext对象
        ServletContext sc = getServletContext();
        //设置属性
        sc.setAttribute("d",data);

        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("写入context成功");
    }
}

(2) 创建一个 readServlet 类;

package org.example;

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

@WebServlet("/read")
public class readServlet extends HttpServlet {
    // write?data=xxx
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        //获取当前servletContext对象
        ServletContext sc = getServletContext();
        //根据属性名,获取属性值
       Object data = sc.getAttribute("d");

        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("读取context:"+data);
    }
}

(3)通过ServletContext进行数据共享;

通过地址栏访问URL:http://localhost:8080/Thymeleaf-study/write?data=shd,可以看到写入成功标志,在http://localhost:8080/Thymeleaf-study/read访问可以读取写入的数据;

如下所示


(2) 监听器

监听器:属于一种设计模式;

使用监听器优点

  • 将事件发生和事件发生后需要执行的代码进行解耦合;
  • 事先注册一个函数或方法到监听器,在某个事件发生后,自动执行;

使用步骤

  • 创建一个类;
  • 添加@WebListener 注解,否则 Tomcat 不能识别;
  • 实现 ServletContextListener 接口, 并实现两个方法 contextInitializedcontextDestroyed

代码如下

package org.example;


import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
       
  }
  
    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

(3)修改 Thymeleaf 引擎初始化代码

package org.example;


import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //1. 先获取context对象
        ServletContext context = sce.getServletContext();
        //2. 创建Templateengine对象
        TemplateEngine engine = new TemplateEngine();
        //3.创建解析器对象
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(context);
        
        //4.设置resolver的一些属性
        resolver.setCharacterEncoding("utf-8");
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");
       
        //5.绑定 resolver 和 engine
        engine.setTemplateResolver(resolver);

        //6.将 engine 放到 ServletContext中,供其他servlet使用
        context.setAttribute("engine",engine);
  }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

后续的 Servlet 直接从 ServletContext 中获取到 engine 对象即可;

6. Thymeleaf 模板语法

(1)常用命令及功能

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

(2)设置标签文本

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

最后结果:将webContext 设置的键值对数据,键为message的值设置到标签内容中;

(3)设置标签属性

常见的属性

  • href
  • src
  • class
  • style…

如设置链接 a 标签的 href 属性:

前端代码

 <a th:href="${a1}">百度</a>
 <a th:href="${a2}">搜狗</a>

servlet代码

 package org.example;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;

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

@WebServlet("/hello")


public class HelloServlet extends HttpServlet {

    //1.创建一个模板引擎
    TemplateEngine engine = new TemplateEngine();
    @Override
    public void init() throws ServletException {

        //2.创建一个网页模板解析器
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());

        //3.设置渲染时编码
        resolver.setCharacterEncoding("utf-8");

        //4.设置网页模板文件路径的前缀与后缀
        resolver.setPrefix("/WEB-INF/templates/");
        resolver.setSuffix(".html");

        //5.将模板解析器绑定到模板引擎中
        engine.setTemplateResolver(resolver);

    }

    //模板引擎都是返回HTML,因此重写HTML
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //设置body格式与编码格式
        resp.setContentType("text/html; charset = utf-8");

        //创建一个web上下文(环境的语义,里面是map结构,可以存放键值对数据)
        WebContext webContext =new WebContext(req,resp,getServletContext());
        //设置键值对的数据:

        webContext.setVariable("a1","http://www.baidu");
        webContext.setVariable("a2","http://www.sogou");

        //模板引擎渲染网页模板:第一个参数为模板名称,第二个参数为web上下文(里面保存了数据)
        //会根据模板解析器设置的前缀+模板名称+后缀,为模板路径,查找到模板,再组织模板内容+数据
        //返回值就是渲染后的网页字符串
        String html =engine.process("hello",webContext);
        resp.getWriter().write(html);

    }
}

(4)条件判断

 条件判断 th:if :根据条件决定该标签是否显示;
 <p th:if="${islogin}">已经登陆</p>

webContext.setVariable("islogin",true);传入的值为true时,就显示为:已经登陆,否则不显示;

网页显示

(5)循环

       th:each 循环的构造出多个元素;
 <ul>
       <li th:each="name : ${names}">
            <span th:text="${name}"></span>
      </li>
  </ul>
  webContext.setVariable("names", Arrays.asList("张三","李四","王五"));

网页显示结果

更多推荐

模板引擎 - Thymeleaf