模板引擎Thymeleaf和监听器

目录

Web开发的历程

模板引擎的概念

Thymeleaf中的核心三个类

TemplateEngine

WebContext

ServletContextTemplateResolver

Thymeleaf的使用流程

Thymeleaf模板语法

设置标签属性 

循环渲染 

ServletContext 

ServletContext的概念 

ServletContext对象中的方法 

监听器(Listener) 

监听器(Listener)的构成 

监听ServletContext的创建 


Web开发的历程

web早期的页面和页面里面的逻辑是混合在一起的,在以前主要使用JSP、PHP来写。但是这种web开发只适合做简单的页面,复杂的页面无论是从开发角度还是从维护角度都比较麻烦。到了后面出现了模板引擎,它的作用就是使页面和逻辑分离。在java用Servlet来写这样的web,它在开发和维护性方面改善了很多,但是前后端的接口开发不方便分工。到了现在前端页面通过ajax和后端通信,后端只返回数据(通常是JSON格式)即可,后端也不用再去拼接页面,都是由前端拿到数据之后,自己负责拼接。前后端分离是现在主流的开发方式,但是模板引擎现在有些地方还在用,所以还是得掌握。

模板引擎的概念

模板引擎是动态页面的渲染方式,是一种将前后端分离开来的一种方式,但是这种分开不是绝对的分开,它会将前端和后端连接的接口关联起来,然后通过一套机制实现前后端交互。模板引擎就像一套试卷里面的开放性作文题,每个人写的都不一样,但是模板是一套的,那就是这张试卷。

模板引擎中的模板指的是将前端页面HTML文件中的一些内容提取出来,放到单独的文件中。对于一些“动态”的部分,这部分内容在模板中用占位符占位。当服务器将这些“动态”的部分计算好了之后,就把模板中的占位符替换成动态计算的结果,然后把这个组装好的HTML格式字符串再返回给浏览器。 

java中的模板引擎有很多,现在主要掌握Thymeleaf。 

Thymeleaf中的核心三个类

TemplateEngine

TemplateEngine负责渲染工作,渲染指的就是把动态的数据替换到html模板中的指定位置。

WebContext

WebContext负责把HTML模板中的变量和java代码中的变量给关联起来(可以简单的理解为键值对结构,HTML文件中的变量是key,而java文件中的变量是value),专门用来描述映射关系。

ServletContextTemplateResolver

ServletContextTemplateResolver称为模板解析器,它负责将之前写好的HTML模板加载过来交给TemplateEngine对象进行渲染 

Thymeleaf的使用流程

0.将Thymeleaf所需要的环境依赖下载到java中的pom.xml文件中; 

1.先编写html模板文件,放到指定目录中;

2.创建Servlet代码:

1)先创建一个TemplateEngine实例;

2)创建一个ServletContextTemplateResolver实例,并且指定需要加载模板文件的路径和字符集,并且把Resolver对象和TemplateEngine关联起来。这一步在init里面处理;

下面这两步在doGet里面处理: 

3)把要和模板中关联起来的变量,使用WebContext来进行表示

4)进行最终的渲染,TemplateEngine有一个process方法专门来做这个事情 

下面演示一个使用Thymeleaf的实例:

首先去第三方库里面下来Thymeleaf的依赖,将依赖复制到pom.xml文件中:

然后在WEB-INF目录下面创建一个template目录,再然后在该目录下面创建hello.html文件:

解析html文件中th:text="${message}" 

th就是thymeleaf的缩写,意思是这个属性是thymeleaf所提供的的。text表示这里的类型是字符串,而${message}表示一个具体的变量。

刚开始创建这个标签的时候,message下面可能会有一条红线,这是因为还没有变量来关联它,所以这里可以不用担心。

第二步创建helloThymeleaf.java文件:

然后按照里面的步骤写上相应的业务代码:

@WebServlet("/helloThymeleaf")
public class HelloThymeleaf extends HttpServlet {
    //首先先创建TemplateEngine实例
    TemplateEngine engine = new TemplateEngine();

    //完成Thymeleaf的初始化
    @Override
    public void init() throws ServletException {
        //创建一个ServletContextTemplateResolver实例,并且指定需要加载模板文件的路径和字符集,并且把Resolver对象和TemplateEngine关联起来
        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. 先从参数中读取出用户要传过来的 message 的值. (从 query string 中读取)
        String value = req.getParameter("key");
        //2.把要和模板中关联起来的变量,使用WebContext来进行表示
        WebContext webContext = new WebContext(req,resp,this.getServletContext());
        webContext.setVariable("message",value);
        //3,进行最终的渲染
        String hello = engine.process("hello", webContext);
        System.out.println(hello);
        resp.getWriter().write(hello);
    }
}

然后启动服务器,在浏览中输入URL:

在通过一个猜数字的游戏来演示Thymeleaf的使用:

首先在template目录下面创建一个GuessNum.html文件,约定前后端交互的方式,然后写上相应的逻辑:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>猜数字游戏</title>
</head>
<body>
      <form action="GuessNum" 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>

创建一个GuessNumer的java文件,然后写上相应的业务代码:

@WebServlet("/GuessNum")
public class GuessNumber extends HttpServlet {
    //首先创建一个TemplateEngine实例
    TemplateEngine engine = new TemplateEngine();

    // toGuess 表示要猜的数字
    private int toGuess = 0;

    // count 表示要猜的次数
    private int count = 0;

    //完成Thymeleaf的初始化
    @Override
    public void init() throws ServletException {
        //创建一个ServletContextTemplateResolver实例,并且指定需要加载模板的路径和字符集,并且把Resolver对象和TemplateEngine关联起来
        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();
        toGuess = random.nextInt(100)+1;
        count = 0;

        //2.返回一个页面
        WebContext webContext = new WebContext(req,resp,this.getServletContext());
        webContext.setVariable("newGame",true);
        String guessNum = engine.process("GuessNum", webContext);
        resp.getWriter().write(guessNum);
    }

    //处理一次猜的过程
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        // 1. 从请求中, 读取出用户提交的数字的内容
        String num = req.getParameter("num");
        int i = Integer.parseInt(num);
        String result = "";

        //2.和toGuess比较
        if(i<toGuess){
            result = "猜低了";
        }
        if(i>toGuess){
            result = "猜大了";
        }
        if(i==toGuess){
            result = "猜对了";
        }
        //3.自增猜的次数
        count++;
        //4.构造一个响应页面
        WebContext webContext = new WebContext(req,resp,this.getServletContext());
        webContext.setVariable("newGame",false);
        webContext.setVariable("result",result);
        webContext.setVariable("count",count);
        String ret = engine.process("GuessNum", webContext);
        resp.getWriter().write(ret);
    }
}

在浏览器中输入URL然后猜数字:

Thymeleaf模板语法

命令                    功能
th:text                把关联变量的值换到HTML标签中,主要是用来替换字符串
th:[HTML标签属性]       用来设置便签的属性(这些属性有href、src、calss、style......)
th:if                  当表达式结果为真是就渲染,为假时就不渲染
th:each                循环渲染

上面th:text、th:if已经演示过了,下面演示其他两个:

设置标签属性 

在template目录下面创建thymeleafAttr.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>设置标签的属性</title>
</head>
<body>
    <a href="${url1}">百度</a>
    <a href="${url2}">搜狗</a>
</body>
</html>

然后创建一个Setting_Lable的java文件,在里面写相应的业务逻辑:

@WebServlet("/Setting")
public class Setting_Lable extends HttpServlet {
    //创建一个TemplateEngine实例
    TemplateEngine engine = new TemplateEngine();

    //完成Thymeleaf的初始化
    @Override
    public void init() throws ServletException {
        //创建一个ServletContextTemplateResolver实例,并且指定需要加载的模板文件的路径和指定字符集,最后将resolver和TemplateEngine关联起来
        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");
        WebContext webContext = new WebContext(req,resp,this.getServletContext());
        webContext.setVariable("url1","https://www.baidu.com");
        webContext.setVariable("url2","https://www.sogou.com");
        String attr = engine.process("thymeleafAttr", webContext);
        resp.getWriter().write(attr);
    }
}

打开浏览器输入URL: 

循环渲染 

th:each的功能是循环构造出多个元素,在屏幕上就相当于循环渲染出多个元素

语法格式:

th:each="自定义的元素变量名称:${集合变量名称}"

创建Each.html文件放到template目录下面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <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>

创建ThymeleafEach的java文件,并写上相应的业务逻辑:

class Person{
    public String name;
    public String phone;

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

    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }
}
@WebServlet("/each")
public class ThymeleafEach extends HttpServlet {
    //首先创建一个TemplateEngine对象实例
    TemplateEngine engine = new TemplateEngine();

    //完成Thymeleaf的初始化

    @Override
    public void init() throws ServletException {
        //创建ServletContextTemplateResolver实例,并且指定模板文件的路径和字符集,最终将resolver和Thymeleaf关联起来
        ServletContextTemplateResolver  resolver = new ServletContextTemplateResolver(this.getServletContext());
        resolver.setPrefix("/WEB-FIN/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> person = new ArrayList<>();
        person.add(new Person("张三","12345"));
        person.add(new Person("李四","54321"));
        
        WebContext webContext = new WebContext(req,resp,this.getServletContext());
        webContext.setVariable("persons",person);
        String each = engine.process("Each", webContext);
        resp.getWriter().write(each);
    }
}

启动服务器,在浏览器中输入URL:

上面html代码中persons相当于java中的一个集合/数组,然后person就相当于访问到persons中的每一个元素。

我们发现上面的利用Thymeleaf的方式来让html文件和java文件分开的操作,每操作一次就要分别创建TemlateEngine实例、ServletContextTemplateResolver解析器对象一次,很麻烦。为了解决这一问题,前辈们就使用ServletContext对象中的方法,让多个Servlet之间,能够共享一个模板引擎。

ServletContext 

ServletContext的概念 

ServletContext叫做Servlet上下文,它是服务器为每一个工程创建的一个对象,是一个工程中Servlet程序全局存储信息的空间,它可以让同一个webapp目录下面的Servlet之间共享变量。服务器一启动就存在,服务器一关闭就销毁。

利用ServletContext的特性就可以实现让多个Servlet之间共享一个TemplateEngine,为了做到这一点,就需要把TemplateEngine的实例在ServletContext中进行创建。

在只创建一个引擎之间先来了解一下ServletContext中的方法:

ServletContext对象中的方法 

void setAttribute(String name,Object obj)                创建键值对
Object getAttribute(String name)                         根据属性名获取属性值,如果name不存在,返回null
void removeAttribute(String name)                        删除对应的属性
getServletContext()或者this.getServletContext()          获取当前ServletContext对象              

下面通过一个代码演示案例来实现多个Servlet之间通过SelvletContext来通信:

单独创建WriteServlet.java和ReadServlet.java两个文件,WriteServlet文件负责往ServletContext

里面写数据,而ReadServlet负责从ServletContext里面读数据:

// 负责往 ServletContext 里面写数据
// 浏览器通过一个形如 /writer?message=aaa 访问到 WriterServlet, 就把 message=aaa 这个键值对存到 ServletContext
@WebServlet("/write")
public class WriteServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        // 1. 先从请求中获取到 message 参数
        String message = req.getParameter("message");
        // 2. 取出 ServletContext 对象 (这个对象是 Tomcat 在加载 webapp 的时候自动创建的)
        ServletContext context = this.getServletContext();
        // 3. 往这里写入键值对
        context.setAttribute("key",message);
        resp.getWriter().write("<h3>向ServletContext中写入数据成功</h3>");
    }
}
// 使用这个 Servlet 从 ServletContext 中读取数据
// 就把刚才的 WriterServlet 存的数据给取出来~
@WebServlet("/read")
public class ReadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        // 1. 获取到同一个 ServletContext 对象
        ServletContext context = this.getServletContext();
        // 2. 从 Context 中获取到刚才存的值
        String key = (String) context.getAttribute("key");
        // 3. 把取出的数据显示出来
        resp.getWriter().write("key: "+key);
    }
}

在浏览器输入框中输入URL:

监听器(Listener) 

JavaEE中的监听器是JavaWeb中的三大组件(Servlet、Filter、Listener)之一,在Servlet运行过程中,会有一些特殊的“时机”,可以供我们来执行一些自定义的逻辑,我们用一种特殊的手段来获取这种时机,这就是监听器。监听器就是一个实现了特定接口的Java类,主要用来监听某个对象的状态变化。

Servlet中的监听器有很多,针对不同的Application、Session、Request对象,有不同的监听器,

可以监听HttpServletRequest的创建和销毁,还有属性变化、监听HttpSession的创建和销毁还有属性变化、监听ServletContext的创建和销毁还有属性变化。

监听生命周期:ServletRequestListener、HttpSessionListener、ServletContextListener

监听属性值:ServletRequestAttributeListener、HttpSessionAttributeListener 、ServletContextAttributeListener

 

我们在这里只需要监听ServletContext的创建即可。

监听器(Listener)的构成 

  • 事件源:被监听的对象
  • 监听器:监听的对象,事件源的变化会触发监听器的响应行为
  • 响应行为:监听器监听到事件源的状态变化时所执行的动作

监听ServletContext的创建 

  • 首先创建一个类,实现ServletContextListener接口,并实现它的两个方法contextInitialized和contextDestroyed
  • 这个类需要使用@WebListener注解修饰,才能正确被Tomcat识别
  • contextInitialized的参数是ServletContextEvent对象,这个对象表示一个事件。可以通过ServletContextEvent.getServletContext()来获取到ServletContext 

下面演示监听器和模板引擎一起 工作:

首先创建一个ThymeleafConfig的java文件,让它实现ServletContextListener,并实现它的两个方法,里面用来实现TemplateEngine对象和创建解析器对象,并为模板文件设置路径和字符集,将模板文件和和模板引擎联系起来:

@WebListener
public class ThymeleafConfig implements ServletContextListener {
    //这个会在context被创建的时候被调用,里面的ServletContextEvent表示一个事件,在这里就表示要执行的Servlet
    //通过sce的getServletContext方法获取到刚刚被创建好的ServletContext对象
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //1.首先先获得webapp的上下文context
        ServletContext context = sce.getServletContext();

        //2.初始化 TemplateEngine
        TemplateEngine engine = new TemplateEngine();

        //3.创建解析器对象,然后设置模板文件的路径和字符集,将模板文件和模板引擎关联起来
        ServletContextTemplateResolver  resolver = new ServletContextTemplateResolver(context);
        resolver.setPrefix("/WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("utf-8");
        engine.setTemplateResolver(resolver);

        //4.把创建好的engine实例给放到ServletContext中.
        context.setAttribute("engine",engine);
        //打印日志,方便观察
        System.out.println("TemplateEngine 初始化完毕!");
    }

    //这个会在context被销毁的时候调用
    @Override
    public void contextDestroyed(ServletContextEvent sce) {

    }
}

然后就可以修改之前的带有模板引擎的文件了,修改之前HelloThymeleaf里面的代码,让他直接从ServletContext中获取到engine实例即可:

@WebServlet("/helloThymeleaf")
public class HelloThymeleaf extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        // 0. 获取到engine
        ServletContext context = getServletContext();
        TemplateEngine engine = (TemplateEngine) context.getAttribute("engine");
        
        // 1. 先从参数中读取出用户要传过来的 message 的值. (从 query string 中读取)
        String value = req.getParameter("key");
        
        //2.把要和模板中关联起来的变量,使用WebContext来进行表示
        WebContext webContext = new WebContext(req,resp,context);
        webContext.setVariable("message",value);
        
        //3,进行最终的渲染
        String hello = engine.process("hello", webContext);
        System.out.println(hello);
        resp.getWriter().write(hello);
    }
}

启动服务器,在浏览器中输入URL:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼吐泡泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值