什么是模板引擎 ?
【作用】模板引擎就是为了使用户界面与业务数据(内容)分离而产生的,它可以分离 Servlet Java 代码和 Html 网页代码(这是相对于 Servlet 直接返回动态页面来说,模板引擎的优点)
原理 / 流程
Thymeleaf 使用流程
Thymeleaf 是 Java 中的模板引擎,当前最流行的一种
1. 通过 maven 引入依赖
在 maven 中央仓库 搜索 Thymeleaf
选择一个合适的版本,如:3.0.12
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
引入依赖后,一定记得要刷新 Maven 面板 注意: web项目需要打包为 war格式 —— < packaging>war< /packaging>
2. 创建 Html 模板文件
创建 hello.html,放到 webapp / WEB-INF / templates 目录中
<h3 th:text="${message}"></h3>
举例:
<body>
<h3>网页模板技术学习</h3>
<p th:text="${message}"></p>
</body>
会发现,有飘红:
th:text 是 Thymeleaf 的语法,浏览器不能直接识别 th:text 属性
3. 编写 Servlet 代码
- 创建一个模板引擎 和 一个网页模板解析器
- 设置渲染时编码
- 设置网页模板文件,路径的前缀和后缀
- 将模板解析器绑定到模板引擎中
- 创建一个web上下文(环境的语义,里面是map结构,存放键值对数据)
- 设置键值对的数据
- 返回渲染后的网页字符串到响应正文
@WebServlet("/hello")
public class helloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
TemplateEngine engine = new TemplateEngine();
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());
resolver.setCharacterEncoding("utf-8");
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
engine.setTemplateResolver(resolver);
WebContext webContext = new WebContext(req,resp,getServletContext());
webContext.setVariable("message",req.getParameter("msg"));
String html = engine.process("hello",webContext);
resp.getWriter().write(html);
}
}
启动服务,刷新页面:
每次请求都创建一个模板引擎 和 一个网页模板解析器,效率是比较低的,也没有必要,我们可以重写一个 init 方法,因为 init 只执行一次,而 doGet 方法每次请求都会执行,可以将创建模板引擎 和 创建网页模板解析器的代码放到 init 方法里
@Override
public void init() throws ServletException {
TemplateEngine engine = new TemplateEngine();
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());
resolver.setCharacterEncoding("utf-8");
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
engine.setTemplateResolver(resolver);
}
但是此时,会发现代码会有飘红:
把 TemplateEngine engine = new TemplateEngine(); 放到 init() 方法外即
重新启动,刷新页面: (效率得到了提高)
Thymeleaf 常用模板语法
命令 | 功能 |
---|
th: text | 在标签体中展示表达式求值结果的文本内容 | th: [HTML标签属性] | 设置任意的 HTML 标签属性的值 | th: if | 当表达式的结果为真时则显示内容,否则不显示 | th: each | 循环访问元素 |
${变量名},来引用 Java 代码中,设置的键值对数据 ( ${键}就是值 )
注意: 网站的绝对路径还是要加 http:// ,需要写全 举例:
webContext.setVariable("a1","http://www.baidu.com");
理解只创建一个引擎实例
整个web应用,只需要初始化一次(代码中的对象engine,resolver,只需要创建一次) (原因:对象及属性不会改变),在文章前面部分,也提出了改进方法,放在 Servlet 的 init() 方法里
但仍然还存在问题: 每个 Servlet 都需要在 init 方法中创建,一个 webapp 中,还是有很多个engine,resolver对象 每个需要渲染页面的 Servlet 类都需要创建一个 TemplateEngine 实例并初始化,其实是完全没有必要的!
一个完整的项目中,只需要创建一个 TemplateEngine,并且只初始化一次即可 为了改进上边出现的问题,需要使用 Servlet 中的 ①ServletContext 和 ②"监听器"
ServletContext
ServletContext是一个 Servlet 程序中全局的储存信息的空间,服务器开始就存在,服务器关闭才销毁
如下图关系:
- Tomcat 在启动时,它会为每个 webapp 都创建一个对应的 ServletContext.
- 一个 Web应用中的所有 Servlet 共享同一个 ServletContext 对象
- 可以通过 HttpServlet.getServletContext() 或者 HttpServletRequest.getServletContext() 获取到当前 webapp 的 ServletContext 对象
理解 Context: 上下文 / 环境;常用于设置一些数据到上下文环境中;上下文环境中的对象就可以互相引用对方的数据 (很多地方都有 Context 这样的概念,它是一个语义的概念)
即: 多个 Servlet 之间,无法直接传递数据,但可以通过共享的一个上下文环境,来设置 / 使用一些数据 (数据传递) ServletContext 类似于 Map 结构,存放多组键值对数据
ServletContext 对象的重要方法
方法 | 描述 |
---|
void setAttribute(String name, Objectobj) | 设置属性(键值对) | Object getAttribute(String name) | 根据属性名获取属性值,如果 name 不存在,返回 null | void removeAttribute(String name) | 删除对应的属性 |
可以看到 ServletContext 和 HttpSession 类很类似,也是在内部组织了若干个键值对结构,相当于一个哈希表;此时同一个 webapp 的多个 Servlet 之间就可以通过 ServletContext 来共享数据
代码示例:多个 Servlet 共享数据
1) 创建 ContextWriteServlet 类
@WebServlet("/write")
public class ContextWriteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = req.getParameter("data");
ServletContext sc = getServletContext();
sc.setAttribute("d",data);
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("写入Context成功!");
}
}
2) 创建 ContextReadServlet 类
@WebServlet("/read")
public class ContextReadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext sc = getServletContext();
Object data = sc.getAttribute("d");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("读取Context:" + data);
}
}
启动服务,打开页面:
如果我们不访问 /write,直接访问 /read,那么此时得到的 data 是 null:
监听器 Listener
监听器,属于一种设计模式
比如: 前边学些的,在 html / js 中,为一个 DOM 元素,绑定一个事件 (事件注册),不需要手动调用该事件的函数,而是在事件发生时,由浏览器来调用事件绑定的函数 这个"事件",其实就是监听器的设计模式 若我们自己设计,可能是:事件发生,执行一段代码 (耦合性太强);而上边例子是事件发生需要执行的代码,和事件发生,两个解耦
在 Servlet 运行过程中,会有一些特殊的 “时机”,可以供我们来执行一些我们自定义的逻辑 监听器就是让程序猿可以在这些 特殊时机 “插入代码”
监听器优点小结:
- 将事件发生和事件发生后需要执行的代码进行解耦合
- 事先注册一个函数或方法到监听器,在某个事件发生后,自动执行
代码示例:监听 ServletContext 的创建
步骤:
- 首先创建一个类
- 添加@WebListener 注解修饰,否则 Tomcat 不能识别
- 实现 ServletContextListener 接口,并实现两个方法 contextInitialized 和 contextDestroyed
@WebListener
public class MyListener implements ServletContextListener {
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
|