目录
ServletContextTemplateResolver
web早期的页面和页面里面的逻辑是混合在一起的,在以前主要使用JSP、PHP来写。但是这种web开发只适合做简单的页面,复杂的页面无论是从开发角度还是从维护角度都比较麻烦。到了后面出现了模板引擎,它的作用就是使页面和逻辑分离。在java用Servlet来写这样的web,它在开发和维护性方面改善了很多,但是前后端的接口开发不方便分工。到了现在前端页面通过ajax和后端通信,后端只返回数据(通常是JSON格式)即可,后端也不用再去拼接页面,都是由前端拿到数据之后,自己负责拼接。前后端分离是现在主流的开发方式,但是模板引擎现在有些地方还在用,所以还是得掌握。
模板引擎是动态页面的渲染方式,是一种将前后端分离开来的一种方式,但是这种分开不是绝对的分开,它会将前端和后端连接的接口关联起来,然后通过一套机制实现前后端交互。模板引擎就像一套试卷里面的开放性作文题,每个人写的都不一样,但是模板是一套的,那就是这张试卷。
模板引擎中的模板指的是将前端页面HTML文件中的一些内容提取出来,放到单独的文件中。对于一些“动态”的部分,这部分内容在模板中用占位符占位。当服务器将这些“动态”的部分计算好了之后,就把模板中的占位符替换成动态计算的结果,然后把这个组装好的HTML格式字符串再返回给浏览器。
java中的模板引擎有很多,现在主要掌握Thymeleaf。
TemplateEngine负责渲染工作,渲染指的就是把动态的数据替换到html模板中的指定位置。
WebContext负责把HTML模板中的变量和java代码中的变量给关联起来(可以简单的理解为键值对结构,HTML文件中的变量是key,而java文件中的变量是value),专门用来描述映射关系。
ServletContextTemplateResolver称为模板解析器,它负责将之前写好的HTML模板加载过来交给TemplateEngine对象进行渲染
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文件,约定前后端交互的方式,然后写上相应的逻辑:
- 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>
-
-
- <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
- 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文件:
- 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目录下面:
- 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 = 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("
向ServletContext中写入数据成功
"); - }
- }
- // 使用这个 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:
