• 三个案例带你入门Thymeleaf。模板引擎,终于不用在后端拼接前端代码了。


    模板引擎,终于不用在后端拼接前端代码了

    如果想要在Servlet项目中直接返回html页面,最原始的方式就是返回字符串,然后设置响应报头Content-Type: text/html让浏览器进行html渲染。但是在后端通过字符串的方式写前端代码效率不仅低,而且容易出错。因此就需要使用模板引擎。

    模板引擎的作用就是在前端代码中添加一些占位符,然后让后端的模板引擎去填写这些占位符。这样就可以不同在后端手动拼接前端代码的字符串了。

    这里模板解析器使用的是Thymeleaf,因为这个是Spring中推荐使用的。可以在maven仓库中找到。

    常用的Thymeleaf模板语法

    • th:text:在标签中展示该表达式中的结果
    • th:[HTML 属性]:设置标签的属性
    • th:if:当表达式结果为true,则显示内容。否则不显示内容。(注意:是显示/不显示,不是渲染/不渲染。渲染/不渲染是对于dom节点来说的,而显示/不显示只是节点中内容是否显示,而节点还是会生成的)
    • th:each:循环渲染元素

    Thymeleaf中的模板语法有很多,这里只罗列几个最常见的,其他的语法可以现用现查。

    案例:使用th:text模板语法将url中的参数动态渲染到界面中

    • Servlet代码
    @WebServlet("/helloThymeleaf")  
    public class HelloThymeleafServlet extends HttpServlet {  
        // 1.创建模板引擎,用于渲染网页  
        private TemplateEngine engine = new TemplateEngine();  
          
        @Override  
        public void init() throws ServletException {  
            // 2.创建模板解析器,用于找到模板文件  
            // 每一个webapp中都会创建一个ServerContext对象,多个Servlet共享一个Context对象  
            // 通过Context对象创建一个webapp中唯一的一个模板解析器,模板解析器就是用来找到模板文件的  
            ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());  
            // 通过设定模板文件的前缀和后缀,判定模板是哪些文件  
            resolver.setPrefix("/WEB-INF/template/");  
            resolver.setSuffix(".html");  
            resolver.setCharacterEncoding("utf-8");  
              
            // 3.将模板引擎和模板解析器关联在一起,即模板引擎需要渲染出模板  
            engine.setTemplateResolver(resolver);  
        }  
      
        @Override  
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
            // 获得url中的参数  
            String message = req.getParameter("message");  
            // 4.使用webContext将模板中的参数进行替换,其实就相当于是一个哈希表  
            WebContext webContext = new WebContext(req, resp, this.getServletContext());  
            webContext.setVariable("message", message);  
      
            // 5.模板引擎渲染模板  
             engine.process("hello", webContext, resp.getWriter());  
            // 上面这句话也可以拆开来写  
            // String html = engine.process("hello", webContext);  
            // resp.getWriter().write(html);    }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 前端代码
    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>Thymeleaftitle>  
    head>  
    <body>  
    	
        <h3 th:text="${message}">h3>  
    body>  
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意:模板文件一定要放在webapp下的WEB-INF文件夹下的template(自己创建)文件夹下。
    ![[Pasted image 20220822224216.png]]

    总结模板引擎套路

    1. 创建TemplateEngine模板引擎,之后使用该对象的process()渲染模板
    2. 创建ServletContextTemplateResolver模板解析器,用于找到模板文件文职,并加载模板文件,
    3. 创建WebContext对象,存储前端变量和Java变量之间的映射关系,之后可以配合模板解析器动态渲染模板。

    此外,需要注意模板文件的目录位置模板文件中Thymeleaf的写法。如果目录位置或者模板语法出错,服务器会就返回500错误。

    使用th:if,th:text模板语法完成一个Web版的猜数字界面

    • Servlet代码
    @WebServlet("/guessNum")  
    public class GuessNumServlet extends HttpServlet {  
        private int guessNum = 0; // 随机数字  
        private int count = 0; // 累计猜数字次数  
        // 创建模板引擎  
        private TemplateEngine engine = new TemplateEngine();  
      
        // 在init()中创建模板解析器  
        @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");  
            Random random = new Random();  
            guessNum = random.nextInt(100) + 1; // [1, 100]  
            // 建立映射关系  
            WebContext webContext = new WebContext(req, resp, this.getServletContext());  
            webContext.setVariable("guessNum", true);  
            // 渲染模板  
            engine.process("guessNum", webContext, resp.getWriter());  
        }  
      
        @Override  
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
            // 比较数字大小  
            resp.setContentType("text/html; charset=utf-8");  
            String parameter = req.getParameter("num");  
            int num = Integer.parseInt(parameter);  
            String result = "";  
            if (num > guessNum) {  
                result = "猜大了";  
            } else if (num < guessNum) {  
                result = "猜小了";  
            } else {  
                result = "猜对了";  
            }  
            count ++;  
      
            // 建立映射关系  
            WebContext webContext = new WebContext(req, resp, this.getServletContext());  
            webContext.setVariable("newGame", false);  
            webContext.setVariable("result", result);  
            webContext.setVariable("count", count);  
            // 渲染模板  
            engine.process("guessNum", webContext, resp.getWriter());  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 前端代码
    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="guessNum" method="POST">  
            <input type="text" name="num">  
            <br>        <input type="submit" value="提交">  
        form>  
        <div th:if="${!newGame}">  
            <div th:text="${result}">div>  
            <div th:text="${count}">div>  
        div>body>  
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    模板引擎存在的最大意义就是让Java代码和前端代码分离开来了。这样使得代码的阅读星提高了,出错率降低了。

    案例:使用th:each模板语法在前端渲染出多个人的姓名和手机号

    • 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(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<Person>();  
            persons.add(new Person("张三", "123"));  
            persons.add(new Person("李四", "456"));  
            persons.add(new Person("王五", "789"));  
      
            WebContext webContext = new WebContext(req, resp, getServletContext());  
            webContext.setVariable("persons", persons);  
            // 如果使用engine.process("thymeleafEach", webContext, resp.getWriter());的话,  
            // 异常会被“消化”,因此如果模板语法出错或者Servlet代码出错服务器是不会响应异常的  
            // engine.process("thymeleafEach", webContext, resp.getWriter());  
      
            // 如果先使用engine.process("thymeleafEach", webContext);然后通过resp.getWriter().write()渲染的话  
            // 那么如果代码出错,服务器就会显示异常。  
            // 因此推荐当服务器出错的话,使用这种写法比较好  
            String html = engine.process("thymeleafEach", webContext);  
            resp.getWriter().write(html);  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 前端代码
    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>Documenttitle>  
    head>  
    <body>  
        <ul>        <li th:each="person: ${persons}">  
                <span th:text="${person.name}">span>  
                :   
                <span th:text="${person.phone}">span>  
            li>    ul>body>  
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 实验结果
      ![[Pasted image 20220823083223.png]]

    编写Thymeleaf如果出错,经常出现问题的地方

    一般Thymeleaf出现问题,都是模板语法出现问题,或者是**前后端的变量没有关联(WebContext出现问题)**上。

    如果使用engine.process("模板", wecContext, resp.getWriter()),这个方法会自动处理抛出来的异常,所以如果模板出现问题也不会抛出异常。

    如果先通过engine.process("模板", webContext)html页面的字符串获得,然后使用resp.getWriter().write()方法的话,那么模板出现问题,服务器就会出现异常,并返回500页面,通过500页面中的错误提示,就可以很快定位到问题所在。

  • 相关阅读:
    k8s、docker 卸载
    c语言编程 “画圆” 源程序
    linux获取文件的属性
    vue3基础流程
    【ACWing】1401. 围住奶牛
    EM@解三角形@正弦定理@余弦定理
    Flutter SQLite 教程之笔记App 数据存储CRUD操作基于 Flutter Sqflite 插件
    【计算机网络】—网络编程(socket)02
    [Power Query] 数据的透视与逆透视
    北京筑龙&京东安全:为采购与招标平台筑起“防火墙”
  • 原文地址:https://blog.csdn.net/zhybiancheng/article/details/126477273