数据和页面结合的工作, 通过服务器完成.
相当于 客户端 发送 HTTP 请求,带上参数. 服务端根据请求取计算响应,然后拼装成完整的 HTML 返回 HTTP 响应给客户端.
服务器把数据返回给浏览器, 由浏览器把数据和页面结合起来.
浏览器和服务器之间的数据往往交互通过 ajax 进行, 数据的格式往往使用 JSON
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.append("html页面 ");
html.append("你好
");
html.append("");
resp.getWriter().write(html.toString());
}
}
运行截图:
可以看出,这种比较简单的,还可以使用拼接的方式来实现.如果复杂了就很麻烦.
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(");
html.append("");
html.append("");
html.append("");
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(");
html.append("");
html.append("");
html.append("");
html.append(""+res+"");
html.append("当前猜了: "+count+"次");
resp.getWriter().write(html.toString());
}
}
模板引擎 就是为了解决 HTML 代码 和 Java 代码混杂在一起的问题.
可以把HTML 提取出来,放到单独的文件夹中,称为 模板.
对于页面中动态的部分,这些部分就可以使用 模板 中的 占位符 占位.当动态的部分计算好了之后,就可以把 该部分的占位符替换成 计算好的内容.然后组装成 HTML 返回给 浏览器.
这里我使用的模板引擎 是 Thymeleaf
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleafartifactId>
<version>3.0.12.RELEASEversion>
dependency>
创建一个目录,在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>Documenttitle>
head>
<body>
<h1 th:text="${message}">h1>
body>
html>
首先在渲染之前,要进行初始化.
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());
}
}
- 在
Servlet
的init
方法中对TemplateEngine
进行初始化工作.resovler
的setPrefix
和setSuffix
指定了从哪个目录下筛选哪些文件engine.process
方法的第一个参数指定了要加载哪个模板文件WebContext
中指定了模板变量名和变量值的对应关系(类似于一个哈希表结构).setVariable
中的第一个参数, 要和模板文件中写的${message}
匹配.engine.process
方法会把刚才的WebContext
里的值替换到模板中, 并把最终结果写入到 resp对象里.- HTML文件 要写在 目录
webapp/WEB-INF/template
下
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>Documenttitle>
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>
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());
}
}
命令 | 功能 |
---|---|
th:text | 在标签体中展示表达式求值结果的文本内容 |
th:[HTML标签属性] | 设置任意的 HTML 标签属性的值 |
th:if | 当表达式的结果为真时则显示内容,否则不显示 |
th:each | 循环访问元素 |
这四种是常见的.
th:text
的功能就是能设置标签的文本内容.
前面的 使用示例已经演示过了
可以用在 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());
}
th:if
在猜数字的演示代码中也用到过了
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());
ServletContext是一个 Servlet 程序中全局的储存信息的空间, 服务器开始就存在, 服务器关闭才销毁.
Tomcat 在启动时,它会为每个Web app都创建一个对应的 ServletContext.
一个WEB应用中的所有 Servlet 共享同一个 ServletContext 对象.可以通过HttpServlet.getServletContext()
或者HttpServletRequest.getServletContext()
获取到当前 webapp 的 ServletContext 对象
相当于在一个启动一个Tomcat的时候,会有很多webapp,每个webapp都会创建一个ServletContext和多个Servlet程序.一个webapp中所有的Servlet 共用这一个ServletContext
方法 | 描述 |
---|---|
void setAttribute(String name, Object obj) | 设置属性(键值对) |
Object getAttribute(String name) | 根据属性名获取属性值, 如果 name 不存在, 返回 null |
void removeAttribute(String name) | 删除对应的属性 |
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
在 Servlet 运行过程中, 会有一些特殊的 “时机”, 可以供我们来执行一些我们自定义的逻辑.监听器就是让程序猿可以在这些 特殊时机 “插入代码”.
Servlet 中的监听器种类有很多.目前我们只关心 监听 ServletContext 的创建.
要使用接口 ServletContextListener
实现这个接口要重写 contextInitialized
方法 和 contextDestroyed
方法
在 ServletContext 初始化完毕之后,会执行 contextInitialized
方法.
在 ServletContext 销毁之前,会执行 contextDestroyed
方法
为了让 Tomcat 识别这个监听器,还需要加上注解 @WebListener
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
通过之前的代码可以看出.每次在实现一个示例的时候,都要通过重写init()
方法来初始化 Thymeleaf
,这样每次创建就很麻烦.
此时就可以通过 监听器的方法,把TemplateEngine
初始化好,放到ServletContext
对象里.后面的Servlet程序就不需要再初始化了.直接取出engine对象就可以了.
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) {
}
}
修改一下之前的代码,
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());
}
}