一:thymeleaf 指令有问题
1.检查th指令语法是否错误
2.检查指令中是否有多余的符号
3.检查th:text中间是否有空格
4.检查后端代码是否有值
Attribute name cannot be null or empty
二:服务器给前端返回的数据,大部分情况下都是一个HTML页面,我们只是用Servlet,想要返回一个动态页面,返回的页面就只能通过字符串拼接的方式来进行构造,这是非常的不科学,使用模板引擎生成动态页面,也实现了前后端分离;
三:前后端彻底的进行分离页面:前端代码通过Ajax的方式来和后端进行通信,后端只负责返回Json格式的数据,后端不会再去拼接页面,都是前端拿到数据之后,自己进行拼接;
使用模板引擎,界面和逻辑进行分离(Java Servlet)
四:模板引擎中常见的类:
1.TemplateEngine,他是负责进行渲染,渲染指的是就是把动态的数据替换到HTML页面中的指定位置,模板引擎做得好象是考试卷子上的填空题一样,每个人在不同的卷子上填的是不同的内容,最终就展示出了不同的页面,模板引擎就是一个模板,依据这个模板就可以生成出不同的页面
2.WebContext,这个类就是一个键值对,目标就是将HTML中模板的变量和Java代码中的变量给关联起来;
3.ServletContextTemplateResolver叫做模板解析器对象,把之前写好的HTML模板给加载过来,并告知engine文件在哪里;
五:模板引擎的使用流程:
1)写一个HTML模板,对于这些可变的数据,使用一些特殊的指令来进行占个位置;
2)针对Thymeleaf来进行初始化
2.1创建TemplateEngine实例
2.2通过WebContextTemplateResolver,并且设置加载模板文件的路径,设置前缀和后缀
2.3把创建好的resolver实例关联到engine对象上
3)针对每一个请求,分别进行模板渲染
a)得到要进行渲染的数据(Java提前准备好一些变量)
b)通过WebContext把HTML中的变量和Java中的变量进行关联起来
c)通过调用engine对象的process方法,实现具体的渲染操作,也就相当于是卷子(HTML模板)和答题卡(WebContext)进行关联起来了;
ServletContext是一个Servlet程序在全局储存信息的空间,服务器开始就存在,服务器关闭就销毁;每一个webapp就是tomact文件中的webapps对应的子目录;
这个类创建的初心:在Tomact启动的时候,他会给每一个webapp(一个文件目录)都提供创建了一个ServletContext对象,而每一个单独的web-app有多个servlet,而这里面的所有servlet对象都共享一个ServletContext对象,可以共享一部分数据;
在这里面,我们也是可以通过ServletContext实现让TemplateEngine在一个webapp中只有一个实例
在之前我们所写的代码当中,每一个Servlet都有一个TemplateEngine,这显然是不太科学的,我们希望同一个webapp中可以共享一个TemplateEngine对象;
那么可以把TemplateEngine搞成单例模式吗?这显然是不行的,单例指的是在整个进程中只有一个实例,但是此处的TemplateEngine不应该是进程中的实例,一个Tomact就是一个进程,Template而应该是webapp级别的实例,为了做到这一点我们应该把TemplateEngine放到ServletContext中进行创建;
它所存在的意义是可以让同一个webapp中的多个Servlet之间可以共享数据,所以ServletContext提供了一些get/set接口;所以程序员想在这多个Servlet之间共享数据,也是可以通过自定义键值对的方式来实现的
1)void setAttribute(String name,Object obj)向我们的ServletContext中设置键值对;
2)Object getAttribute(String name)根据属性名来获取到属性的值
3)void removeAttribute(),删除对应的属性
1)咱们之前用的HttpSession可以让程序员自己去定义一些键值对来进行存储,就像是一个时间胶囊一样,我们在登陆成功的时候会存放一些数据进去,后续再进行登录进行访问的时候再通过getAttribute()方法进行取出来使用;
2)程序员想要在这多个Servlet之间共享哪些数据,也是通过自定义键值对的方式来进行实现的;那么我们就可以想WebContext对象中来设置一些自定义的键值对,然后再别的Servlet中按照这个Key来进行访问也就可以了
3)也就是说只要是在Java200这个web-app中创建的Servlet,咱们都是可以通过getServletContext得到同一个上下文对象
1)创建一个GET请求,请求的格式类似于,http://127.0.0.1:8080/Java200/write?message=aaa
我们在WriteServlet中调用req.getparamter()方法,从请求参数中得到一个字符串message,通过req.getServletContext(),或者this.getServletContext(),就可以获取到当前webapp中的ServletContext对象;
2)通过ServletContext.setAttribute()把message的值设置进去;
当前我们写的两个Servlet对象都是用的一个webap,因为如果他们进行打包,都打到同一个war包里面了;只要是在这个webapp中创建出来的Servlet对象,在两个Servlet都是可以通过req.getServletContext得到得到同一个上下文对象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("/write")
//这个类负责向ServletContext中写数据
//浏览器通过一个形如/write?message=aaa访问到WriteServlet,就把这个属性message=aaa这个键值对储存到ServletContext
public class WriteServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
//1从请求中获取到message参数
String flag=req.getParameter("message");
//2获取到当前的ServletContext对象(这个对象是Tomact在加载webapp的时候自动创建的)
ServletContext servletContext= req.getServletContext();
//3 向ServletContext中写入键值对
servletContext.setAttribute("flag",flag);
//4 返回响应
resp.getWriter().write("储存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;
//用这个类来向我们的ServletContext中读取数据,这样就可以把刚才WriteServlet中储存的值读取出来
@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 servletContext= req.getServletContext();
//2从这里面来获取到刚才存得值
String result=(String)servletContext.getAttribute("flag");
resp.getWriter().write("flag"+result);
}
}
1)我们进行学习ServletContext对象的初心就是想用这个对象来进行存储一个TemplateEngine对象,达到说让当前的所有webapp中的所有Servlet共用同一个TemplateEngine对象
2)所以我们最终就需要把TemplateEngin初始化好,同时放到ServletContext对象里面,所以后续的Servlet就不必进行初始化TemplateEngin,后面需要在别的Servlet进行访问的时候,直接进行获取就可以了;
3)为了保证每一个其他的Servlet都能够在第一时间可以获取到一个初始化好的TemplateEngin实例,我们就需要在ServletContext在创建好之后,就第一时间的给TemplateEngin进行实例化,初始化,进行保存;
————————————————————————————————————
1)但是我们刚才分析了,ServletContext是由Tomact自动创建出来的,那么我们如何才可以让ServletContext创建好之后,就第一时间执行我们自己定义的的代码呢?所以Servlet就给我们提供了一组机制,叫做listener成为监听器; 因此我们就可以通过listener来监听我们ServletContext的初始化完毕的操作(ServletContext一旦创建好,就出发了监听器,执行我们自己所写的代码事件)
2)JS中学过的onclick操作,也是一个监听器,用户一旦进行点击,就被监听到了,然后接下来进行后续的操作;
1)这个过程中涉及到的一些接口,我们要创建Mylistener类,就要实现ServletContextListener接口,并实现两个方法,contextInitialized和contextDestoryed;
2)Mylistener类通过使用@webListener来进行注解修饰,才可以正确的被Tomact识别,contextInitialized的参数是ServletContextEvent对象,这个对象标识一个事件,此处我们并不进行深究;
我们只需要可以通过ServletContextEvent.getServletContext()方法可以获取到一个ServletContext即可;
3)当ServletContext被创建好之后,就会直接自动调用执行调用contextInitialized方法;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.annotation.WebServlet;
@WebListener
public class Mylistener implements ServletContextListener {
//当ServletContext初始化之后,会立即执行这个方法
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext已经进行了初始化,马上执行这个方法");
//1 获取到ServletContext这个对象
ServletContext servletContext=servletContextEvent.getServletContext();
servletContext.setAttribute("message","我爱我的家");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
1)创建一个新的类实现ServletContextListener接口,重写其中的contextInitialized方法,这个方法是在初始化完毕后被自动调用的;
2)给新的类加上一个@webListener注解,加上这个注解之后才可以被tomact识别出来并进行加载;
3)在方法里面获取ServletContext是通过ServletContextEvent对象来拿到的;后续就可以调用setAttribute()方法和getAttribute()方法来进行使用即可;
4)在这里面我们就可以把TemplateEngin的初始化操作就行了,还可以进行初始化一些其他的操作和内容
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class Mylistener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//1 初始化TemplateEngine并创建实例,并获取到ServletContext对象(通过方法中的参数获取到)
ServletContext servletContext=servletContextEvent.getServletContext();
TemplateEngine templateEngine=new TemplateEngine();
//2 创建ServletContextTemplateResolver实例(模板解析器),并将两个类进行关联
ServletContextTemplateResolver servletContextTemplateResolver=new ServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setPrefix("/WEB-INF/template/");
servletContextTemplateResolver.setSuffix(".html");
servletContextTemplateResolver.setCharacterEncoding("utf-8");
templateEngine.setTemplateResolver(servletContextTemplateResolver);
//3把创建好的实例创建键值对放到ServletContext中,方便后面的Servlet进行获取,就不用在每一个Servlet里面进行初始化化操作了
servletContext.setAttribute("TemplateEngine",templateEngine);
System.out.println("当前webapp的TemplateEngine已经初始化完毕");
//4后面的Servlet如果想要获取到templateEngine这个对象,就可以用
//TemplateEngine templateEnginexx=(TemplateEngine) servletContext.getAttribute("TemplateEngine");
//后续就可以调用templateEngine.process方法,HTML文件,webContext;
}
//ServletContext被销毁之前,会自动地执行这个方法
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
我们要引入模板文件,我们要在webapp中创建WEB-INF目录,在这个目录里面创建一个目录叫做template目录,把我们表白墙的前端页面方法到这个目录里面;