Servlet的原理和基础使用
目录
2.Tomcat伪代码
2.Servlet API详解
2.1HttpServlet
2.2 HttpServletRequest
3.案例分析——实现服务器版本的表白墙
1.Servlet运行原理
本篇内容学习在 Servlet 的代码中对应的 doGet 代码是如何被调用的? ?响应如何返回给浏览器?
首先要明确: Servlet是 HTTP应用层里进行的一些操作,属于上层用户态操作;
而底层依赖着内核态的操作:传输层、网络层、数据链路层、物理层,基础网络通信。
我们的实现是在 Tomcat 基础上运行的,浏览器给服务器发送请求时 ,Tomcat (HTTP Servlet)作为 HTTP 服务器(HTTP 协议作为一个应用层协议, 需要底层协议栈来支持), 就可以接收到请求.下图描述了该请求发送的过程:
在用户态: Tomcat是一个应用程序(基于java实现,属于Java进程)是用户态的一个普通进程;
在内核态:Tomcat是运行在JVM之上的,而servlet是Tomcat上层的内容,由Tomcat完成底层的工作,再交由servlet去做进一步的处理,用户在servlet体系下,去完成应用层的工作。(也即在servlet体系下,根据请求计算响应,通过servlet和Tomcat进行交互,Tomcat拿到数据后,和浏览器进行网络传输(封装分用))
打开一个浏览器(用户主机上的应用程序),浏览器也即是客户端的角色,当用户访问一个浏览器时,浏览器就会通过一系列操作构造一个HTTP请求,详细的过程如图:
(1)(发送请求)在客户端(用户主机上)的过程:
- 把应用层的 HTTP请求 交给传输层,将其封装成一个TCP数据报(TCP报头+HTTP请求)
- TCP数据报 交给网络,由IP协议进行封装成IP数据报(IP报头+TCP数据报)
- 把 IP数据报 交给数据链路层,由以太网协议封装成以太网数据帧(帧头+IP数据报+帧尾)
- 最后把以太网数据帧(二进制数据)交给物理层,通过把1、0转变成高/低电平(高低电磁波信号),再由网线、光纤等硬件设备将其传输出去
数据在传输过程中,会经过n次转发,最终才能到达目的地(网络传输)
(2)(接收请求并解析,计算响应)服务器上的过程:
- 先是服务器的物理层收到数据,当其转化为以太网数据帧时,向上传递给数据链路层
- 数据链路层接收到以太网数据帧,进行帧头、帧尾的去除操作,数据变成了IP数据段,再向上传给网络层
- 网络层接收到IP数据报,去除IP报头,变为TCP数据段,传递给传输层
- 传输层拿到数据后,去除TCP报头,数据变成了原始的HTTP请求,再将其向上传递给应用层的Tomcat服务器
- Tomcat接收到HTTP请求之后,针对HTTP协议做进一步的处理:
- 根据协议里的路径URL,第一级路径ContextPath 指定要访问哪个webapp(网站)
- 第二级路径ServletPath 指定我们要访问的是哪个HTTPServlet类
- 根据类里的doPost/doGet等方法,执行对应代码(也即是根据请求计算响应)
以上便是在浏览器访问页面的整个实现过程,涉及客户端和服务器之间的网络传输,之前的网络原理涉及的内容是应用层一下的各层之间的交互过程,而servlet这里,重点关注的是应用层Tomcat的交互
(3) 返回响应:
- doGet / doPost 执行完毕后, Tomcat 自动把 HttpServletResponse 对象转换成一个字符串(符合 HTTP 协议), 并通过 Socket 把这个响应发送出去.
- 响应数据在服务器的主机上通过网络协议栈层层 封装(也即是(1)发送请求的过程), 最终得到(以太网数据帧)二进制 bit 流, 通过物理层硬件设备转换成光/电信号传输出去.
- 承载信息的光/电信号通过互联网上的网络设备, 最终到达浏览器所在的主机
- 浏览器主机收到光/电信号, 又通过网络协议栈逐层进行分用, 层层解析, 最终还原成HTTP 响应, 并交给浏览器处理.
- 浏览器也通过 Socket 读到响应(字符串), 按照 HTTP 响应的格式来解析这个响应. 并把body 中的数据按照一定的格式显示在浏览器的界面上.
2.Tomcat伪代码
伪代码并不能真正的编译运行,只是为了便于理解,程序员编写的实现某个功能的大致逻辑体系,以助于更好地便于缕清思路,构造出完整的实现逻辑。
1、初始化 / 准备工作:加载 Servlet 实例(从制定目录webapp目录中找到要加载的类)
(加载分懒汉模式和饿汉模式,并非Tomcat启动start就调用,根据情况而定)
2、处理请求:根据 URL找到匹配的 Servlet 类,再调用 Servlet 里面对应的方法
(1)Tomcat初始化流程 的伪代码
class Tomcat {// 用来存储所有的 Servlet 对象private List<Servlet> instanceList = new ArrayList<>();public void start() {
// 根据约定,读取 WEB-INF/web.xml 配置文件;
// 并解析被 @WebServlet 注解修饰的类
// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.Class<Servlet>[] allServletClasses = ...;
// 这里要做的的是实例化出所有的 Servlet 对象出来;for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。Servlet ins = cls.newInstance();instanceList.add(ins);}
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;for (Servlet ins : instanceList) {ins.init();}
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 RequestServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况ExecuteService pool = Executors.newFixedThreadPool(100);while (true) {Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的pool.execute(new Runnable() {doHttpRequest(socket);});}
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;for (Servlet ins : instanceList) {ins.destroy();}}public static void main(String[] args) {new Tomcat().start();}
}
(2)Tomcat 处理请求流程 的伪代码
class Tomcat {void doHttpRequest(Socket socket) {
// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建HttpServletRequest req = HttpServletRequest.parse(socket);HttpServletRequest resp = HttpServletRequest.build(socket);
// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态内容
// 直接使用我们学习过的 IO 进行内容输出if (file.exists()) {
// 返回静态内容return;}
// 走到这里的逻辑都是动态内容了
// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
// 最终找到要处理本次请求的 Servlet 对象Servlet ins = findInstance(req.getURL());
// 调用 Servlet 对象的 service 方法
// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了try {ins.service(req, resp);} catch (Exception e) {
// 返回 500 页面,表示服务器内部错误}}}
(3)Servlet 的 service 方法的实现 的伪代码
class Servlet {public void service(HttpServletRequest req, HttpServletResponse resp) {String method = req.getMethod();if (method.equals("GET")) {doGet(req, resp);} else if (method.equals("POST")) {doPost(req, resp);} else if (method.equals("PUT")) {doPut(req, resp);} else if (method.equals("DELETE")) {doDelete(req, resp);}
......}}
- 1、init:对象创建好就执行初始化。用户也可重写init方法,来执行一些初始化的操作。
- 2、destroy:退出主循环,Tomcat 结束运行之前回调用。主要是用来 释放资源。
- 3、service:在处理请求的阶段来调用,每次来个请求都需要调用一次 service。
2.Servlet API详解
重点掌握HttpServlet、HttpServletRequest、HttpServletResponse这三个类
2.1HttpServlet
写 Servlet 代码时, 先创建类, 继承自 HttpServlet, 并重写其中的某些方法.使得Tomcat可以调用。 该过程和继承有关,会涉及到多态。如何理解多态???
- 集合类: List list = new ArrayList<>();
- 多线程:class Mythred extends Thread{ 重写 run方法 }
- Servlet 也是多态!因为我们自己写的代码也是通过继承重写的方式来实现的。
实际开发中,主要重写 doXXX 方法,方法的调用时机(称为 "Servlet 生命周期")
注意!!! HttpServlet 实例只在程序启动时创建一次. 而不是每次收到 HTTP 请求都重创建
面试题:Servlet的生命周期:
servlet包括怎么出现的、怎么消失的、以及中间的过程和状态
- servlet在实例化之后调用一次init
- servlet每次收到请求,调用一次sevice
- servletz在销毁之前,调用一次destory
变量生命周期:怎么出现的、怎么消失的
(1)构造一个post请求
浏览器中直接输入URL属于get请求;而post请求的构造需要利用form表单或者ajax(需要创建一个html文件,webapp目录下),注意!!!要想使用ajax,必须引入jQuery: jQuery引入
若不想引入依赖,可使用第三方工具postman,可以任意构造HTTP请求:
postman Windows下载
1)test.html
注意!! 使用ajax构造请求时路径不加/,访问test.html文件的路径是:/classServlet/test.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>Document</title>
</head>
<body><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script><script>$.ajax({type: 'post',url: 'method',success: function(body) {console.log(body);}});</script></body>
</html>
2)methodServlet.java(构造post请求,实现dopost方法)
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("/method")
public class methodServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doPost(req, resp);resp.setContentType("text/html;charset=utf8");resp.getWriter().write("post响应");}
}
浏览页面:127.0.0.1:8080/classServlet/test.html
classServlet 是 contextPath
2.2 HttpServletRequest
Tomcat 通过 Socket API 读取 HTTP 请求(字符串), 并按照 HTTP 协议的格式把字符串解析成HttpServletRequest 对象.
注意!!!
String getRequestURI() :返回该请求的 URL 的一部分。
URL:全球资源定位器(Uniform Resource Locator)
URI:唯一资源标识符(Uniform Resource Identifier)
都表示网上唯一资源, URL 表示的是这个资源的位置,而 URI 表示的是这个资源的 ID。
getQueryString() 得到的是 完整的查询字符串,而下面三个方法把 查询字符串 解析为键值对:
- getParameterNames是得到所有key,以Enum枚举方式表示 ;
- getParameter根据key得到value;
- getParameterValues如果存在多个key相同的情况,得到value是一个数组的形式。
获取请求报头(键值对),servlet解析报头,得到键值对的结构getHeaderName:获取报头中的所有key,getHeader:根据key获取value
(1)打印请求信息
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.Enumeration;@WebServlet("/showRequest")
public class showRequestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);StringBuilder stringBuilder = new StringBuilder();stringBuilder.append(req.getProtocol()); //返回请求协议的名称和版本stringBuilder.append("<br>");stringBuilder.append(req.getMethod()); //返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。stringBuilder.append("<br>");stringBuilder.append(req.getRequestURI()); //,返回该请求的 URL 的一部分。stringBuilder.append("<br>");stringBuilder.append(req.getContextPath()); //返回指示请求上下文的请求 URI 部分。stringBuilder.append("<br>");stringBuilder.append(req.getQueryString()); //返回包含在路径后的请求 URL 中的查询字符串。stringBuilder.append("<br>");stringBuilder.append("<h3>header 部分</h3>");Enumeration<String> headerNames = req.getHeaderNames();while(headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();String headerValue = req.getHeader(headerName);stringBuilder.append(headerName + ":" + headerValue + "<br>");}resp.setContentType("text/html;charset=utf-8");resp.getWriter().write(stringBuilder.toString());}
}
得到如下页面:
(2)获取 GET 请求中的参数,使用getParameter
浏览器通过 query string 给服务器传递了两个参数, userId 和 classId,在服务器端就可以通过 getParameter 来获取到参数的值.
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("/getParameter")
public class getParameterServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
// 假设浏览器传来一个请求: getParameter?userId=10&classId=20String userId = req.getParameter("userId");String classId = req.getParameter("classId");http://resp.getWriter().write("userId=" + userId +",classId=" + classId );}
}
浏览器输入:127.0.0.1:8080/classServlet/getParameter?userId=10&classId=20,得到页面:
(3)获取 POST 请求中的参数
服务器如何获取参数(post请求body格式):
- x-www-form-urlencoded (使用form表单或postman在前端构造此请求)
- form-data
- json
1)x-www-form-urlencoded ---->getParameter
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("/postGetParameter")
public class postGetParameterServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//假设传过来的参数 userId=10&classId=20 服务器也是通过req.getPararmeter获取内容String userId = req.getParameter("userId");String classId = req.getParameter("classId");http://resp.getWriter().write("userId=" + userId +",classId=" + classId );}
}
webapp目录下利用form表单完成的静态页面test.html:127.0.0.1:8080/classServlet/postGetParameter
<!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>Document</title>
</head>
<body><form action="postGetParameter" method="post"><input type="text" name="userId"><input type="text" name="classId"><input type="submit" value="提交"></form><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script></body>
</html>
使用postman获取请求
2)json获取参数——借助第三方库Jackson(Spring官方推荐库)
maven中央仓库中下载
在浏览器前端代码中,,通过js构造json格式的请求;后端代码中,通过Jackson来处理
<!--构造json格式的请求 不适用form,使用ajax--><input type="text" name="userId"><input type="text" name="classId"><input type="submit" value="提交"><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script><script>let userIdInput = document.querySelector('#userId');let classIdInput = document.querySelector('#classId');let button = document.querySelector('#submit');button.onclick = function() {$.ajax({type: 'post',url: 'postJson',contentType: 'application/json',data: JSON.stringify({userId:userIdInput.value,classId:classIdInput,}),success: function(body) {console.log(body);}});}</script>
import com.fasterxml.jackson.databind.ObjectMapper;
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;class User {public int userId;public int classId;}@WebServlet("/postJson")
public class postJsonServlet extends HttpServlet {//创建一个Jackson核心对象ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//读取body中的请求,使用ObjectMapper解析成需要对对象;// readValue:把JSON格式字符串转为java对象// 第一个参数:表示对那个字符串进行转换,可写成String对象,也可InputStream对象// 第二个参数:把这个JSON格式字符串,转为java对象User user = objectMapper.readValue(req.getInputStream(),User.class);resp.getWriter().write("userId=" + user.userId +",classId=" + user.classId );}
}
readValue如何完成转换的???
- 先把getInputStream对应的流对象里面的数据读取出来
- 针对这个JSON字符串进行解析,字符串------>键值对
- 遍历键值对,获取每一个key,根据key和类里面的属性名称对比一下,看是否有匹配的
- 若有匹配的属性,则把当前key对应的value赋值给User类的属性中
- 若无匹配,就跳过,取下一个key
反射:根据类对象获取里面的关键信息
使用ajax提交数据,默认不会跳转页面 ,在控制台中会显示服务器的相应数据
2.3 HttpServletResponse
Tomcat 把 HttpServletResponse 对象(servlet中利用doxx方法计算出来的响应)按照 HTTP 协议的格式, 转成一个字符串, 并通过Socket 写回给浏览器.
(1) 服务器返回的状态码,只是告诉浏览器当前的状态,不影响浏览器正常显示body中的内容
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("/status")
public class setStateServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(404);resp.getWriter().write("hello");}
}
(2) 让浏览器每秒钟自动刷新一次. 并显示当前的时间戳.(自动刷新header.Refresh)
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("/autoRefresh")
public class AutoRefresh extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Refresh", "1");resp.getWriter().write("timeStamp: " + System.currentTimeMillis());}
}
(3)构造重定向响应:返回一个重定向 HTTP 响应, 自动跳转到另外一个页面
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("/Redirect")
public class RedirectServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setStatus(302);resp.setHeader("location","https://www.sogou.com");}
}
3.案例分析——实现服务器版本的表白墙
服务器内存版(非持久化存储)
实现一个服务器,需要考虑提供的什么服务?服务如何触发?如何实现交互??
对于本项目来说:表白墙主要提供两个接口
- 1. 告诉服务器当前的留言是什么数据
- 触发:用户点击提交时,会给服务器发送一个Http请求,服务器保存请求
- 2. 从服务器获取到,当前有哪些留言
- 触发:页面加载获取所有留言的数据
3.1代码编写
一个项目的创建流程:
(1)创建项目
(2)引入依赖:pom.xml文件内写入依赖
(3)构建目录
(4)编写代码
(5)打包部署
(2)引入依赖:pom.xml文件内写入依赖
<dependencies>
<!-- servlet依赖 https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- Jackson依赖 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.2.1</version></dependency></dependencies>
(4)编写代码和打包部署
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("/message")
public class MessageServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//处理提交消息请求resp.getWriter().write("helloPost");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取消息列表resp.getWriter().write("helloGet");}
}
(5)实现代码:后端逻辑
import com.fasterxml.jackson.databind.ObjectMapper;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.ArrayList;
import java.util.List;class Message{ //创建一个类Message表示提交的一个请求记录public String from;public String to;public String message;
}@WebServlet("/message")
public class MessageServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();//通过ObjectMapper类解析(构造)JSON对象List<Message> messageList = new ArrayList<>();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//处理提交消息请求//1.从请求流对象中读取当前的数据内容,再把读取到的对象转成Message.class形式的对象Message message = objectMapper.readValue(req.getInputStream(),Message.class);
//2.保存数据(保存到服务器内存),需提前创建一个对象用于保存数据messageList.add(message);
//3.通过setContentType告知页面返回数据的格式是JSONresp.setContentType("application/json;charset=utf8"); // jquery ajax自动把字符串转化为js对象resp.getWriter().write("{\"ok\":true}");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取消息列表,把所有内容返回给客户端,,objectMapper把java对象转为JSON对象String jsonString = objectMapper.writeValueAsString(messageList);System.out.println("jsonString:"+jsonString);resp.setContentType("application/json;charset=utf8"); // jquery ajax自动把字符串转化为js对象resp.getWriter().write("helloGet");}
}
使用postman进行连接检查:
(6)前端代码
<!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>告白墙</title>
</head>
<body><!-- 通过内部样式style标签,引入CSS样式 --><style>*{/* 首先先去除浏览器样式 *//* 将 内外边距设置为0,设置盒子模型为向内挤压 */margin: 0;padding: 0;box-sizing: border-box;}.containner{width: 100%;}h3{/* 文本居中 */text-align: center;/* 上下边距为 20px,左右边距为0 */padding: 20px 0;font-size: 24px;}p{text-align: center;color: #666;padding: 10px 0;}.row{width: 400px;height: 50px;/* 上下外边距为零,左右外边距自适应 *//* 就是元素水平居中操作 */margin: 0 auto;/* 弹性布局 */display: flex;/* 水平居中 */justify-content: center;/* 垂直居中 */align-items: center;}.row span{width: 60px;font-size: 17px;}.row input{width: 300px;height: 40px;line-height: 40px;font-size: 20px;text-indent: 0.5em;outline: none;}.row #submit{width: 360px;height: 40px;font-size: 20px;line-height: 40px;margin: 0 auto;color: white;background-color: orange;border: none;border-radius: 15px;outline: none;}/* 当鼠标点击按钮的时候,会改变按钮颜色 */.row #submit:active{background-color: grey;}</style><div class="container"><h3>表白墙</h3><p>输入后点击提交,会将信息显示在表格中</p><br><div class="row"><span>谁: </span><input type="text"></div><div class="row"><span>对谁: </span><input type="text"></div><div class="row"><span>说什么: </span><input type="text"></div><div class="row"><button id="submit">提交</button></div></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script><script>
//加入ajax代码,此处要加入的逻辑有两部分:
//1.点击提交按钮时,ajax要构造数据发送给服务器
//2.页面加载时,从服务器获取消息列表,并在界面上直接显示 function getMessages() {$.ajax({type: 'get', url: 'message',success: function(body) {let newDiv = document.createElement('div');for(let message of body) { //依次取数组中的每个元素newDiv.innerHTML = from + "对" + to +"说:" + say;newDiv.className = 'row';let container = document.querySelector('.container'); // 将新建节点,挂在 container 这个节点下面container.appendChild(newDiv);}}});}//当用户点击submit,就会获取input的内容,从而把内容构造成一个div,查到页面尾部 let submitBtn = document.querySelector('#submit');submitBtn.onclick = function(){// 1、获取 3个input 文本框中的数据let inputs = document.querySelectorAll('input');let from = inputs[0].value;let to = inputs[1].value;let say = inputs[2].value;if(from == ''|| to == '' || say == ''){// 用户填写的数据,并不完整。所以不提交。return;}// 2、生成一个新的 div,内容就是 三个 input 文本框的内容拼接// 再把这个 元素,挂在DOM树上。let newDiv = document.createElement('div');newDiv.innerHTML = from + "对" + to +"说:" + say;newDiv.className = 'row';// 将新建节点,挂在 container 这个节点下面let container = document.querySelector('.container');container.appendChild(newDiv);//3.清空之前输入框的内容for(let i = 0; i < inputs.length; i++) {inputs[i].value = '';}//4.把当前获取到的输入框的内容,构造成一个http post请求,通过ajax发给服务器let body = {from: from,to: to,message: msg};$.ajax({type: "post",url: "message",contentType: "application/json;charset=utf8",data: JSON.stringify(body),success: function(body) { alert("消息提交成功");},error: function() {alert("消息提交失败!");}});}</script>
</body>
</html>
127.0.0.1:8080/messageServlet/test.html
test.html前端页面的书写只能保证写入的数据能够正常显示出来,但不能保存,不能刷新,服务器重启也会导致页面写入的内容不见了
对象和JSON对象之间的转换:
Java:
objectMapper.readValue :json字符串转成对象
objectMapper.writeValueAsString:对象转成json字符串
JS:
JSON.parse:json字符串转成对象
JSON.stringify:对象转成json字符串
硬盘能持久存储,内存不能持久存储,也有一种介于硬盘和内存之间的设备(像内存一样访问快、也能像硬盘一样持久存储)
使用JDBC基本流程:
- 创建数据源
- 和数据库建立连接
- 构造SQL语句
- 执行SQL语句
- 如果是查询语句,需要遍历结果集,若是插入/删除/修改,则不需要
- 关闭资源,释放连接
为解决上述数据内容的丢失,引入数据库进行数据保存。保证页面刷新后,数据不丢失,但不能保证服务器重启后数据不丢失(把数据保存到硬盘上(可以是文件、也可是数据库))
(0)第一步在pom.xml文件中添加数据库依赖(确保和自己的版本对应)
(1)选中数据库java102,创建表message(from、to 是表中的关键字,需要加上反引号``)
(2)DButil - 数据库访问程序 DBUtil.java文件
主要是简化MessageMysqlServlet中的程序:
- 将 数据库连接,创建数据源,资源释放,具体实现细节给封装成一个类。
- 来供 MessageServlet 中的程序使用
import com.mysql.cj.jdbc.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class DBUtil {private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102?characterEncoding=utf8&useSSL=false"; //连接数据库的地址private static final String USERNAME = "root"; //用户名private static final String PASSWORD = "123456"; //密码private static DataSource dataSource = null; //创建实例dataSourceprivate static DataSource getDataSource() { //单例模式中的——懒汉模式if (dataSource == null) {dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl(URL);((MysqlDataSource)dataSource).setUser(USERNAME);((MysqlDataSource)dataSource).setPassword(PASSWORD);}return dataSource; }public static Connection getConnection() throws SQLException { //数据库建立连接return getDataSource().getConnection();}public void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if (statement != null) {try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if (connection != null) {try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}}
是否存在线程安全的问题???
看当前调用的方法doXXX处于什么环境,当处于多线程环境下是存在线程安全的问题的
import com.fasterxml.jackson.databind.ObjectMapper;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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;@WebServlet("/messageMysql")
public class MessageMysqlServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();//通过ObjectMapper类解析(构造)JSON对象@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//处理提交消息请求//1.从请求流对象中读取当前的数据内容,再把读取到的对象转成Message.class形式的对象Message message = objectMapper.readValue(req.getInputStream(),Message.class);
//2.保存数据(保存到服务器内存),需提前创建一个对象用于保存数据//3.通过setContentType告知页面返回数据的格式是JSONresp.setContentType("application/json;charset=utf8"); // jquery ajax自动把字符串转化为js对象resp.getWriter().write("{\"ok\":true}");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//获取消息列表,把所有内容返回给客户端,,objectMapper把java对象转为JSON对象List<Message> messageList = load();String jsonString = objectMapper.writeValueAsString(messageList);System.out.println("jsonString:"+jsonString);resp.setContentType("application/json;charset=utf8"); // jquery ajax自动把字符串转化为js对象resp.getWriter().write(jsonString);}private void save(Message message) {//把消息保存到数据库Connection connection = null;PreparedStatement statement = null;//1.和数据库建立连接try {connection = DBUtil.getConnection();//2.构造SQL语句String sql = "insert into message values(?,?,?)"; //3个“?“进行占位statement = connection.prepareStatement(sql); //设置表的三个属性statement.setString(1,message.from);statement.setString(2,message.to);statement.setString(3,message.message);//3.执行sqlstatement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,null);}}private List<Message> load() {List<Message> messageList = new ArrayList<>();//1.从数据库中获取所有信息Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null; //定义一个resultSet存放结果try {connection = DBUtil.getConnection();String sql = "select * from message"; //查询语句statement = connection.prepareStatement(sql);resultSet = statement.executeQuery();//2.遍历结果集while (resultSet.next()) {Message message = new Message();message.from = resultSet.getString("from");message.to = resultSet.getString("to");message.message = resultSet.getString("message");messageList.add(message);}} catch (SQLException throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return messageList;}
}
开发一个简单网站的基本步骤:
- 约定前后端交互的接口
- 开发服务器代码
- 编写servlet能够处理前端发来的请求
- 编写数据库代码,存储/获取关键信息
- 开发客户端代码
- 基于ajax构造请求以及解析响应
- 响应用户操作(点击按钮之后,触发给服务器发送请求的行为)
Servlet的原理和基础使用相关推荐
- Web开发基础_Servlet学习_0011_Servlet中的多线程安全问题与Servlet运行原理
Servlet中的多线程安全问题 Servlet运行原理 Servlet中的多线程安全问题 演示 案例演示: 工程案例目录结构 pom.xml: <project xmlns="htt ...
- Servlet运行原理以及生命周期
Servlet运行原理 Servlet生命周期定义了一个Servlet如何被加载.初始化,以及它怎样接收请求.响应请求,提供服务.在讨论Servlet生命周期之前,先让我们来看一下这几个方法: 1. ...
- servlet工作原理_Servlet 生命周期、工作原理
Servlet 生命周期:Servlet 加载--->实例化--->服务--->销毁. init():在Servlet的生命周期中,仅执行一次init()方法.它是在服务器装入Ser ...
- JSP+JavaBean+Servlet工作原理实例…
JSP+JavaBean+Servlet工作原理实例讲解 首先,JavaBean和Servlet虽都是Java程序,但是是完全不同的两个概念.引用mz3226960提出的MVC的概念,即M-model ...
- Spark累加器实现原理及基础编程
Spark累加器实现原理及基础编程 实现原理 累加器用来把 Executor 端变量信息聚合到 Driver 端.在 Driver 程序中定义的变量,在Executor 端的每个 Task 都会得到这 ...
- Spark广播变量实现原理及基础编程
Spark广播变量实现原理及基础编程 实现原理 广播变量用来高效分发较大的对象.向所有工作节点发送一个较大的只读值,以供一个或多个 Spark 操作使用.比如,如果你的应用需要向所有节点发送一个较大的 ...
- Servlet底层原理、Servlet实现方式、Servlet生命周期
Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...
- Servlet 运行原理
文章目录 Servlet 如何运行 演示 Servlet 运行原理 Servlet 如何运行 用户向浏览器地址栏输入:http://ip:port/helloweb/sayHello?name=zs ...
- Linux iptables:规则原理和基础
什么是iptables? iptables是Linux下功能强大的应用层防火墙工具,但了解其规则原理和基础后,配置起来也非常简单. 什么是Netfilter? 说到iptables必然提到Netfil ...
- java启动servlet_Java Servlet 运行原理分析
1 Servlet基本执行过程 Web容器(如Tomcat)判断当前请求是否第一次请求Servlet程序 . 如果是第一次,则Web容器执行以下任务: 加载Servlet类. 实例化Servlet类. ...
最新文章
- python函数手册 stata_Stata连享会-Python量化
- 从qplot开始入门
- QT 5 1 0 MinGW 的安装及使用
- Windows server 2008 R2 DHCP服务器的架设
- validation problems were found problem cvc-complex-type.2.4a
- Thinkphp在Nginx服务器下部署的问题--宝塔面板篇
- 智慧零售四年,来酷科技好大一盘棋
- 悉尼科技大学量子计算_世界排名前12位的量子计算研究型大学
- Android APP登录界面
- “FCoE全解系列”之网络融合交换机类型
- uni-app设置页面背景及背景图片
- 谷歌浏览器Chrome通过命令截图整个网页,screen,通过插件生成gif图片,以及通过插件进行录屏
- IOS6区别于IOS5的几个不明显的改变
- 如何生成一个二维码?
- string+DFS leetcode-17.电话号码下的字母组合
- 计算机网络应用技术技能大赛
- druid 配置理解及监控界面参数理解
- nokia x android 界面,诺基亚(NOKIA)X手机主界面评测-ZOL中关村在线
- K-means聚类实现图片分割
- 全球土地利用数据ESRI 10m Land Cover 2020 in GEE(Google Earth Engine)