目录
动态页面的渲染方式
动态页面需要通过服务器根据客户端传来的参数, 动态计算得到一些结果, 并且把这些结果显示到页面上.
服务器渲染
数据和页面结合的工作, 通过服务器完成.
客户端渲染
服务器把数据返回给浏览器, 由浏览器把数据和页面结合起来.
浏览器和服务器之间的数据往往交互通过 ajax 进行, 数据的格式往往使用 JSON.
字符串拼接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");
//请求传过来一个query string,里面有一个参数是name,返回的页面里,就有这个name的值,请求的name不同,返回的页面就不同
String name = req.getParameter("name");
resp.getWriter().write("<h3>"+name+"</h3");
}
}
如果是返回一个简单的页面, 可以按照上述方式拼接字符串完成.
但是如果是一个复杂的页面, 就要写很复杂的代码了
使用模板引擎
模板引擎使用流程
通过maven引入依赖
选择3.0.12版本
创建HTML模板文件
hello.html
<!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>thymeleaf</title>
</head>
<body>
<!--th就是thymeleaf的缩写,表示这个属性是thymeleaf类提供的,text表示类型为字符串-->
<h3 th:text="${message}"></h3>
</body>
</html>
编写Servlet代码
helloThymeleafServlet.java
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("/helloThymeleaf")
public class HelloThymeleafServlet extends HttpServlet {
//这是Thymeleaf中最核心的类 TemplateEngine模板引擎
private TemplateEngine engine = new TemplateEngine();
@Override
public void init() throws ServletException {
//创建一个模板解析器对象,搭配上下文来使用
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
//让模板解析器来加载模板文件,这里的前缀就表示模板文件所在的目录,正因为如此,模板文件就必须放在WEB-INF中
//设置前缀后缀,让模板引擎知道要加载哪些文件到内存中,以备后用
resolver.setPrefix("/WEB-INF/template/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
//把解析器对象,设置到engine中
engine.setTemplateResolver(resolver);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//初始化之后,就要执行模板渲染了
//1.先从参数中读取出用户要传过来的message的值
String message = req.getParameter("message");
//2.把当前请求中读取出来的message的值和模板中的${message}关联起来
WebContext webContext = new WebContext(req,resp,this.getServletContext());
webContext.setVariable("message",message);
//3.进行最终渲染,将webContext的内容替换到hello
String html = engine.process("hello",webContext);
System.out.println(html);
resp.getWriter().write(html);
}
}
部署程序
可以看到
Thymeleaf模板语法
设置标签文本
th:text 的功能就是能设置标签的文本内容.
设置标签属性
一些常用的需要设置的属性:href src class style ......
<a th:href="${url1}">百度</a>
<a th:href="${url2}">搜狗</a>
条件判断
th:if 的功能是根据条件决定该标签是否显示.
<div th:if="${!newGame}">
<div>已经猜了: <span th:text="${count}"></span> 次</div>
<div>结果: <span th:text="${result}"></span> </div>
</div>
循环
th:each 的功能是可以循环的构造出多个元素.
在java代码中就是用类存储person,用list存储presons
<ul>
<li th:each="person : ${persons}">
<span th:text="${person.name}"></span>
<span th:text="${person.phone}"></span>
</li>
</ul>
查看模板语法的报错信息
通过形如下面的代码来渲染模板, 如果模板渲染出错, 能看到服务器返回了 500 状态码, 但是看不到异常调用栈.
engine.process("thymeleafEach", webContext, resp.getWriter());
原因是抛出的异常被 process 内部处理掉了.
可以使用以下代码代替, 即可看到异常调用栈.
String html = engine.process("thymeleafEach", webContext);
resp.getWriter().write(html);
只创建一个引擎实例
什么是ServletContext
是为了让webapp的多个Servlet之间能够共享数据
先访问writer,给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里面写数据
//例如通过/writer?message=aaa访问到WriterServlet,就把message=aaa这个键值对存到ServletContext
@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");
//1.从请求中获取到message参数
String message = req.getParameter("message");
//2.取出ServletContext对象(这个对象是Tomcat在加载webapp的时候自动创建的)
ServletContext context = this.getServletContext();
//3.往这里写入键值对
context.setAttribute("message",message);
//4.返回响应
resp.getWriter().write("<h3>存储message成功</h3>");
}
}
再访问reader,就能拿到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;
//使用Servlet从ServletContext中读取数据
//从WriterServlet里面存的数据中取出来
@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");
//1.获取到同一个ServletContext对象
ServletContext context = this.getServletContext();
//2.从Context中获取到刚才存的值
String message = (String) context.getAttribute("message");
//3.把取出的数据显示出来
resp.getWriter().write("message: " + message);
}
}
什么是监听器Listener
可以看到,刚才的两个类中的ServletContext对象是同一个,所以我们需要使用ServletContext来存储一个TemplateEngine对象,达到让当前webapp的所有Servlet共同使用同一个TemplateEngine。
就可以再ServletContext创建好之后,第一时间给TemplateEngine进行初始化,为了达到这一目的,Servlet给我们提供了一个机制,Listener监听器,因此就可以使用Listener来监听ServletContext的初始化完毕操作
Servlet 中的监听器种类有很多:
监听 HttpSession 的创建销毁, 属性变化
监听 HttpServletRequest 的创建销毁, 属性变化
监听 ServletContext 的创建销毁, 属性变化
此处我们只需要使用监听器监听 ServletContext 的创建即可.
涉及到的接口: ServletContextListener
我们实现这个接口, 并重写其中的 servletContextInitialized 方法. 当 ServletContext 创建的时候就会自动执行到 servletContextInitialized 方法
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
//需要让Tomcat识别这个类,所以要加WebListener这个注解
@WebListener
public class MyListener implements ServletContextListener {
//初始化完毕后会执行这个方法
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext初始化");
//获取一下ServletContext对象,通过方法和参数获取到的
ServletContext context = sce.getServletContext();
context.setAttribute("message","初始化的消息");
}
//销毁之前执行这个方法
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
效果
修改Thymeleaf引擎初始化代码
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ThymeleafConfig implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//初始化TemplateEngine
ServletContext context = sce.getServletContext();
//1.创建engine实例
TemplateEngine engine = new TemplateEngine();
//2.创建resolver实例
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(context);
resolver.setPrefix("/WEB-INF/template/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
engine.setTemplateResolver(resolver);
//3.把创建好的engine实例放到ServletContext中
context.setAttribute("engine",engine);
System.out.println("TemplateEngine初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}