• 模板引擎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文件:

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

    1. @WebServlet("/helloThymeleaf")
    2. public class HelloThymeleaf extends HttpServlet {
    3. //首先先创建TemplateEngine实例
    4. TemplateEngine engine = new TemplateEngine();
    5. //完成Thymeleaf的初始化
    6. @Override
    7. public void init() throws ServletException {
    8. //创建一个ServletContextTemplateResolver实例,并且指定需要加载模板文件的路径和字符集,并且把Resolver对象和TemplateEngine关联起来
    9. ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
    10. resolver.setPrefix("/WEB-INF/template/");
    11. resolver.setSuffix(".html");
    12. resolver.setCharacterEncoding("utf-8");
    13. engine.setTemplateResolver(resolver);
    14. }
    15. @Override
    16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. resp.setContentType("text/html;charset=utf-8");
    18. // 1. 先从参数中读取出用户要传过来的 message 的值. (从 query string 中读取)
    19. String value = req.getParameter("key");
    20. //2.把要和模板中关联起来的变量,使用WebContext来进行表示
    21. WebContext webContext = new WebContext(req,resp,this.getServletContext());
    22. webContext.setVariable("message",value);
    23. //3,进行最终的渲染
    24. String hello = engine.process("hello", webContext);
    25. System.out.println(hello);
    26. resp.getWriter().write(hello);
    27. }
    28. }

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

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

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>猜数字游戏title>
    6. head>
    7. <body>
    8. <form action="GuessNum" method="post">
    9. <input type="text" name="num">
    10. <input type="submit" value="提交">
    11. form>
    12. <div th:if="${!newGame}">
    13. <div th:text="${result}">div>
    14. <div th:text="${count}">div>
    15. div>
    16. body>
    17. html>

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

    1. @WebServlet("/GuessNum")
    2. public class GuessNumber extends HttpServlet {
    3. //首先创建一个TemplateEngine实例
    4. TemplateEngine engine = new TemplateEngine();
    5. // toGuess 表示要猜的数字
    6. private int toGuess = 0;
    7. // count 表示要猜的次数
    8. private int count = 0;
    9. //完成Thymeleaf的初始化
    10. @Override
    11. public void init() throws ServletException {
    12. //创建一个ServletContextTemplateResolver实例,并且指定需要加载模板的路径和字符集,并且把Resolver对象和TemplateEngine关联起来
    13. ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
    14. resolver.setPrefix("/WEB-INF/template/");
    15. resolver.setSuffix(".html");
    16. resolver.setCharacterEncoding("utf-8");
    17. engine.setTemplateResolver(resolver);
    18. }
    19. //获取到页面的初始情况,并且初始化,生成一个带猜的数字
    20. @Override
    21. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    22. resp.setContentType("text/html;charset=utf-8");
    23. //1.生成一个待猜的数字
    24. Random random = new Random();
    25. toGuess = random.nextInt(100)+1;
    26. count = 0;
    27. //2.返回一个页面
    28. WebContext webContext = new WebContext(req,resp,this.getServletContext());
    29. webContext.setVariable("newGame",true);
    30. String guessNum = engine.process("GuessNum", webContext);
    31. resp.getWriter().write(guessNum);
    32. }
    33. //处理一次猜的过程
    34. @Override
    35. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    36. resp.setContentType("text/html;charset=utf-8");
    37. // 1. 从请求中, 读取出用户提交的数字的内容
    38. String num = req.getParameter("num");
    39. int i = Integer.parseInt(num);
    40. String result = "";
    41. //2.和toGuess比较
    42. if(i
    43. result = "猜低了";
    44. }
    45. if(i>toGuess){
    46. result = "猜大了";
    47. }
    48. if(i==toGuess){
    49. result = "猜对了";
    50. }
    51. //3.自增猜的次数
    52. count++;
    53. //4.构造一个响应页面
    54. WebContext webContext = new WebContext(req,resp,this.getServletContext());
    55. webContext.setVariable("newGame",false);
    56. webContext.setVariable("result",result);
    57. webContext.setVariable("count",count);
    58. String ret = engine.process("GuessNum", webContext);
    59. resp.getWriter().write(ret);
    60. }
    61. }

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

    Thymeleaf模板语法

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

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

    设置标签属性 

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>设置标签的属性title>
    6. head>
    7. <body>
    8. <a href="${url1}">百度a>
    9. <a href="${url2}">搜狗a>
    10. body>
    11. html>

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

    1. @WebServlet("/Setting")
    2. public class Setting_Lable extends HttpServlet {
    3. //创建一个TemplateEngine实例
    4. TemplateEngine engine = new TemplateEngine();
    5. //完成Thymeleaf的初始化
    6. @Override
    7. public void init() throws ServletException {
    8. //创建一个ServletContextTemplateResolver实例,并且指定需要加载的模板文件的路径和指定字符集,最后将resolver和TemplateEngine关联起来
    9. ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
    10. resolver.setPrefix("/WEB-INF/template/");
    11. resolver.setSuffix(".html");
    12. resolver.setCharacterEncoding("utf-8");
    13. engine.setTemplateResolver(resolver);
    14. }
    15. @Override
    16. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. resp.setContentType("text/html;charset=utf-8");
    18. WebContext webContext = new WebContext(req,resp,this.getServletContext());
    19. webContext.setVariable("url1","https://www.baidu.com");
    20. webContext.setVariable("url2","https://www.sogou.com");
    21. String attr = engine.process("thymeleafAttr", webContext);
    22. resp.getWriter().write(attr);
    23. }
    24. }

    打开浏览器输入URL: 

    循环渲染 

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

    语法格式:

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

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>循环构建元素title>
    6. head>
    7. <body>
    8. <ul>
    9. <li th:each="person:${persons}">
    10. <span th:text="${person.name}">span>
    11. <span th:text="${person.phone}">span>
    12. li>
    13. ul>
    14. body>
    15. html>

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

    1. class Person{
    2. public String name;
    3. public String phone;
    4. public Person(String name, String phone) {
    5. this.name = name;
    6. this.phone = phone;
    7. }
    8. public String getName() {
    9. return name;
    10. }
    11. public String getPhone() {
    12. return phone;
    13. }
    14. }
    15. @WebServlet("/each")
    16. public class ThymeleafEach extends HttpServlet {
    17. //首先创建一个TemplateEngine对象实例
    18. TemplateEngine engine = new TemplateEngine();
    19. //完成Thymeleaf的初始化
    20. @Override
    21. public void init() throws ServletException {
    22. //创建ServletContextTemplateResolver实例,并且指定模板文件的路径和字符集,最终将resolver和Thymeleaf关联起来
    23. ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
    24. resolver.setPrefix("/WEB-FIN/template/");
    25. resolver.setSuffix(".html");
    26. resolver.setCharacterEncoding("utf-8");
    27. engine.setTemplateResolver(resolver);
    28. }
    29. @Override
    30. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    31. resp.setContentType("text/html;charset=utf-8");
    32. List person = new ArrayList<>();
    33. person.add(new Person("张三","12345"));
    34. person.add(new Person("李四","54321"));
    35. WebContext webContext = new WebContext(req,resp,this.getServletContext());
    36. webContext.setVariable("persons",person);
    37. String each = engine.process("Each", webContext);
    38. resp.getWriter().write(each);
    39. }
    40. }

    启动服务器,在浏览器中输入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对象中的方法 

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

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

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

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

    1. // 负责往 ServletContext 里面写数据
    2. // 浏览器通过一个形如 /writer?message=aaa 访问到 WriterServlet, 就把 message=aaa 这个键值对存到 ServletContext
    3. @WebServlet("/write")
    4. public class WriteServlet extends HttpServlet {
    5. @Override
    6. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    7. resp.setContentType("text/html;charset=utf-8");
    8. // 1. 先从请求中获取到 message 参数
    9. String message = req.getParameter("message");
    10. // 2. 取出 ServletContext 对象 (这个对象是 Tomcat 在加载 webapp 的时候自动创建的)
    11. ServletContext context = this.getServletContext();
    12. // 3. 往这里写入键值对
    13. context.setAttribute("key",message);
    14. resp.getWriter().write("

      向ServletContext中写入数据成功

      "
      );
    15. }
    16. }
    1. // 使用这个 Servlet 从 ServletContext 中读取数据
    2. // 就把刚才的 WriterServlet 存的数据给取出来~
    3. @WebServlet("/read")
    4. public class ReadServlet extends HttpServlet {
    5. @Override
    6. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    7. resp.setContentType("text/html;charset=utf-8");
    8. // 1. 获取到同一个 ServletContext 对象
    9. ServletContext context = this.getServletContext();
    10. // 2. 从 Context 中获取到刚才存的值
    11. String key = (String) context.getAttribute("key");
    12. // 3. 把取出的数据显示出来
    13. resp.getWriter().write("key: "+key);
    14. }
    15. }

    在浏览器输入框中输入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对象和创建解析器对象,并为模板文件设置路径和字符集,将模板文件和和模板引擎联系起来:

    1. @WebListener
    2. public class ThymeleafConfig implements ServletContextListener {
    3. //这个会在context被创建的时候被调用,里面的ServletContextEvent表示一个事件,在这里就表示要执行的Servlet
    4. //通过sce的getServletContext方法获取到刚刚被创建好的ServletContext对象
    5. @Override
    6. public void contextInitialized(ServletContextEvent sce) {
    7. //1.首先先获得webapp的上下文context
    8. ServletContext context = sce.getServletContext();
    9. //2.初始化 TemplateEngine
    10. TemplateEngine engine = new TemplateEngine();
    11. //3.创建解析器对象,然后设置模板文件的路径和字符集,将模板文件和模板引擎关联起来
    12. ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(context);
    13. resolver.setPrefix("/WEB-INF/template/");
    14. resolver.setSuffix(".html");
    15. resolver.setCharacterEncoding("utf-8");
    16. engine.setTemplateResolver(resolver);
    17. //4.把创建好的engine实例给放到ServletContext中.
    18. context.setAttribute("engine",engine);
    19. //打印日志,方便观察
    20. System.out.println("TemplateEngine 初始化完毕!");
    21. }
    22. //这个会在context被销毁的时候调用
    23. @Override
    24. public void contextDestroyed(ServletContextEvent sce) {
    25. }
    26. }

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

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

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

  • 相关阅读:
    动态库静态库对比
    服务器风波始末-暨给臭hs道歉
    百分点大数据技术团队:解读ToB产品架构设计的挑战及应对方案
    firewalld防火墙处理的命令
    M5311连接HTTPS服务器下载bin文件(干货)
    MyBatis trim标签起什么作用呢?
    解读OOM killer机制输出的日志
    香橙派 Kunpeng Pro 上手初体验
    ASP.NET Core MVC应用模型的构建[2]: 定制应用模型
    第三章 UI组件之弹出组件【Android基础学习】
  • 原文地址:https://blog.csdn.net/yahid/article/details/126419639