JavaWeb --- 模板引擎

1. 动态页面的渲染方式

1.1 服务器渲染

数据和页面结合的工作, 通过服务器完成.
相当于 客户端 发送 HTTP 请求,带上参数. 服务端根据请求取计算响应,然后拼装成完整的 HTML 返回 HTTP 响应给客户端.
在这里插入图片描述

1.2 客户端渲染

服务器把数据返回给浏览器, 由浏览器把数据和页面结合起来.
浏览器和服务器之间的数据往往交互通过 ajax 进行, 数据的格式往往使用 JSON
在这里插入图片描述

1.3 服务端渲染示例

示例1: 通过字符串拼接出HTML

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("/html")
public class htmlServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        StringBuilder html = new StringBuilder();
        html.append("<html>");
        html.append("<head><title>html页面</title></head>");
        html.append("<body><h3>你好</h3></body>");
        html.append("</html>");
        resp.getWriter().write(html.toString());
    }
}

运行截图:
在这里插入图片描述
可以看出,这种比较简单的,还可以使用拼接的方式来实现.如果复杂了就很麻烦.

示例2: 服务器版 猜数字游戏

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;
import java.util.Random;

@WebServlet("/guess")
public class GuessServlet extends HttpServlet {
    
    
    // 这里的 ToGuess 表示要猜的数字
    private int ToGuess = 0;
    // 这里的 count 表示本次猜了的次数
    private int count = 0;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        Random random = new Random();
        ToGuess = random.nextInt(100)+1;
        count = 0;
        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>");
        resp.getWriter().write(html.toString());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        String str = req.getParameter("num");
        int num = Integer.parseInt(str);
        String res = "";
        count++;
        if(num > ToGuess){
    
    
            res = "猜大了";
        }else if (num < ToGuess){
    
    
            res = "猜小了";
        }else {
    
    
            res = "猜对了";
        }
        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>"+res+"</div>");
        html.append("<div>当前猜了: "+count+"次</div>");
        resp.getWriter().write(html.toString());
    }
}

运行截图:
在这里插入图片描述
在这里插入图片描述
在相当于复杂的情况下,再去用拼接就很麻烦.

2. 模板引擎

2.1 什么是模板引擎

模板引擎 就是为了解决 HTML 代码 和 Java 代码混杂在一起的问题.
可以把HTML 提取出来,放到单独的文件夹中,称为 模板.

对于页面中动态的部分,这些部分就可以使用 模板 中的 占位符 占位.当动态的部分计算好了之后,就可以把 该部分的占位符替换成 计算好的内容.然后组装成 HTML 返回给 浏览器.

这里我使用的模板引擎 是 Thymeleaf

2.2 Thymeleaf 的使用

① 引入依赖

在这里插入图片描述
在这里插入图片描述

<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.12.RELEASE</version>
</dependency>

② 创建一个 HTML 的模板文件

创建一个目录,在webapp/WEB-INF/template,在里面新建一个 hello.html
在这里插入图片描述

<!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>
    <h1 th:text="${message}"></h1>
</body>
</html>

③ 编写 Servlet 代码

首先在渲染之前,要进行初始化.

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 HelloThymeleafServlet extends HttpServlet {
    
    
    // 负责渲染工作
    private TemplateEngine engine = new TemplateEngine();

    /**
     * 在执行模板渲染之前,需要先进行初始化
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
    
    
        // 创建一个 模板解析器 对象
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
        // 让 模板解析器 加载 模板文件
        // 这里的文件前缀 表示 模板文件所在的目录
        // 这里的文件后缀 表示 模板文件的类型
        resolver.setPrefix("/WEB-INF/template/");
        resolver.setSuffix(".html");
        // 把 解析器 设置到 engine 对象中
        engine.setTemplateResolver(resolver);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        String message = req.getParameter("message");
        //把当前从请求中读取出来的 message 的值和 模板 中的 ${message} 关联起来
        WebContext context = new WebContext(req,resp,getServletContext());
        context.setVariable("message",message);
        // 进行渲染
        engine.process("hello",context, resp.getWriter());
    }
}

④ 运行 并查看运行结果

此时 页面的内容就和message的取值有关
在这里插入图片描述
在这里插入图片描述

总结:

  1. Servlet init 方法中对 TemplateEngine 进行初始化工作.
  2. resovlersetPrefixsetSuffix 指定了从哪个目录下筛选哪些文件
  3. engine.process 方法的第一个参数指定了要加载哪个模板文件
  4. WebContext 中指定了模板变量名和变量值的对应关系(类似于一个哈希表结构). setVariable 中的第一个参数, 要和模板文件中写的 ${message} 匹配.
  5. engine.process 方法会把刚才的 WebContext 里的值替换到模板中, 并把最终结果写入到 resp对象里.
  6. HTML文件 要写在 目录webapp/WEB-INF/template
    在这里插入图片描述

2.3 猜数字游戏 (模板引擎)

1) 创建HTML模板文件

在这里插入图片描述

<!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>
    <form action="guessNum" method="POST">
        <input type="text" name="num">
        <input type="submit" value="提交">
    </form>
    <div th:if="${newGame}">
        <div th:text="${result}"></div>
        <div th:text="${count}"></div>
    </div>
</body>
</html>

2) 编写 Servlet 代码

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;
import java.util.Random;

@WebServlet("/guessNum")
public class GuessNumServlet extends HttpServlet {
    
    
    private int ToGuess=0;
    private int count=0;
    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");
        Random random = new Random();
        ToGuess = random.nextInt(100)+1;
        count = 0;

        WebContext context = new WebContext(req,resp,getServletContext());
        context.setVariable("newGame",false);
        engine.process("guess",context, resp.getWriter());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        String str = req.getParameter("num");
        int num = Integer.parseInt(str);
        String res = "";
        count++;
        if(num > ToGuess){
    
    
            res = "猜大了";
        }else if (num < ToGuess){
    
    
            res = "猜小了";
        }else {
    
    
            res = "猜对了";
        }

        WebContext webContext = new WebContext(req,resp,getServletContext());
        webContext.setVariable("newGame",true);
        webContext.setVariable("result",res);
        webContext.setVariable("count",count);

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

3) 运行结果

在这里插入图片描述
在这里插入图片描述
相比于前一个服务器版,代码简化了不少.

2.4 Thymeleaf 模板语法

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

这四种是常见的.

a) 设置标签文本

th:text 的功能就是能设置标签的文本内容.
前面的 使用示例已经演示过了

b) 设置标签的属性

可以用在 href,src,class,style…
示例:
前端重点代码

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

后端重点代码

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        WebContext webContext = new WebContext(req,resp,getServletContext());
        webContext.setVariable("url1","https://www.baidu.com");
        webContext.setVariable("url2","http://www.sogou.com");
        engine.process("Web",webContext,resp.getWriter());
    }

运行截图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

c) 条件判断

th:if在猜数字的演示代码中也用到过了

d) 循环

th:each 的功能是可以循环的构造出多个元素
语法格式为:

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

示例:
前端重点代码

 <ul>
        <li th:each="person : ${persons}">
            <span th:text="${person.name}"></span>
            <span th:text="${person.phone}"></span>
        </li>
    </ul>

后端重点代码

        resp.setContentType("text/html;charset=utf-8");
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三","110"));
        list.add(new Person("李四","120"));
        list.add(new Person("王五","119"));

        WebContext webContext = new WebContext(req,resp,getServletContext());
        webContext.setVariable("persons",list);
        engine.process("Each",webContext, resp.getWriter());

运行截图:
在这里插入图片描述

3. ServletContext

3.1 什么是 ServletContext

ServletContext是一个 Servlet 程序中全局的储存信息的空间, 服务器开始就存在, 服务器关闭才销毁.
Tomcat 在启动时,它会为每个Web app都创建一个对应的 ServletContext.
一个WEB应用中的所有 Servlet 共享同一个 ServletContext 对象.可以通过HttpServlet.getServletContext()或者HttpServletRequest.getServletContext()获取到当前 webapp 的 ServletContext 对象

相当于 Tomcat 路径下的 webapps 目录.
在这里插入图片描述

相当于在一个启动一个Tomcat的时候,会有很多webapp,每个webapp都会创建一个ServletContext和多个Servlet程序.一个webapp中所有的Servlet 共用这一个ServletContext

3.2 ServletContext 对象的重要方法

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

3.3 代码示例: 演示多个 Servlet 共享数据

Servlet1: 写入message

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("/writer")
public class writerServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        String message = req.getParameter("message");
        ServletContext servletContext = req.getServletContext();
        servletContext.setAttribute("message",message);
        resp.getWriter().write("设置成功!");
    }
}

Servlet2: 读取刚刚写入的message

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("/reader")
public class readerServlet extends HttpServlet{
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        ServletContext servletContext = req.getServletContext();
        String message = (String) servletContext.getAttribute("message");
        resp.getWriter().write("message: " + message);
    }
}

观察运行结果
当 writer 设置message之后 reader 也能获取到 message的内容
在这里插入图片描述
在这里插入图片描述
当message没有设置的时候,reader获取到的就是null
在这里插入图片描述

4. 监听器

4.1 什么是监听器( Listener )

在 Servlet 运行过程中, 会有一些特殊的 “时机”, 可以供我们来执行一些我们自定义的逻辑.监听器就是让程序猿可以在这些 特殊时机 “插入代码”.

Servlet 中的监听器种类有很多.目前我们只关心 监听 ServletContext 的创建.

4.2 ServletContextListener 接口

要使用接口 ServletContextListener
实现这个接口要重写 contextInitialized 方法 和 contextDestroyed 方法

ServletContext 初始化完毕之后,会执行 contextInitialized 方法.
ServletContext 销毁之前,会执行 contextDestroyed 方法
为了让 Tomcat 识别这个监听器,还需要加上注解 @WebListener

4.3 代码示例: 监听 ServletContext 的创建

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

//加上注解才能让Tomcat识别
@WebListener
public class myListener implements ServletContextListener {
    
    
    // ServletContext初始化之后,会执行这个方法
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
    
    
        System.out.println("ServletContext 初始化完毕");
        ServletContext context = servletContextEvent.getServletContext();
        context.setAttribute("message","初始化message");
    }

    // ServletContext销毁之前 会执行这个方法
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    
    
        // 此处不关心这个方法
    }
}

运行结果演示
由于之前reader在writer没写入数据的时候是 null,此时给message初始化,再没有运行writer的时候,reader也能读到message
在这里插入图片描述
在这里插入图片描述

5. 只创建一个引擎实例

通过之前的代码可以看出.每次在实现一个示例的时候,都要通过重写init()方法来初始化 Thymeleaf,这样每次创建就很麻烦.

此时就可以通过 监听器的方法,把TemplateEngine 初始化好,放到ServletContext 对象里.后面的Servlet程序就不需要再初始化了.直接取出engine对象就可以了.

5.1 使用监听器来初始化模板引擎

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 ThymeleafConfig implements ServletContextListener {
    
    
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
    
    
        System.out.println("初始化完毕!");
        ServletContext context = servletContextEvent.getServletContext();
        // 初始化
        TemplateEngine engine = new TemplateEngine();
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(context);
        resolver.setPrefix("/WEB-INF/template/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("utf-8");
        engine.setTemplateResolver(resolver);
        // 把 engine 放到了 ServletContext 中 后面的Servlet程序就可以使用
        context.setAttribute("engine",engine);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    
    

    }
}

5.2 测试代码

修改一下之前的代码,

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

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("/forTest")
public class ForTestServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html;charset=utf-8");
        String message = req.getParameter("message");
        WebContext webContext = new WebContext(req,resp,getServletContext());
        webContext.setVariable("message",message);
        ServletContext context = getServletContext();
        TemplateEngine engine = (TemplateEngine) context.getAttribute("engine");
        engine.process("hello",webContext, resp.getWriter());
    }
}

运行结果
使用这样的初始化
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/wwzzzzzzzzzzzzz/article/details/124498080