目录

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的原理和基础使用相关推荐

  1. Web开发基础_Servlet学习_0011_Servlet中的多线程安全问题与Servlet运行原理

    Servlet中的多线程安全问题 Servlet运行原理 Servlet中的多线程安全问题 演示 案例演示: 工程案例目录结构 pom.xml: <project xmlns="htt ...

  2. Servlet运行原理以及生命周期

    Servlet运行原理 Servlet生命周期定义了一个Servlet如何被加载.初始化,以及它怎样接收请求.响应请求,提供服务.在讨论Servlet生命周期之前,先让我们来看一下这几个方法: 1. ...

  3. servlet工作原理_Servlet 生命周期、工作原理

    Servlet 生命周期:Servlet 加载--->实例化--->服务--->销毁. init():在Servlet的生命周期中,仅执行一次init()方法.它是在服务器装入Ser ...

  4. JSP+JavaBean+Servlet工作原理实例…

    JSP+JavaBean+Servlet工作原理实例讲解 首先,JavaBean和Servlet虽都是Java程序,但是是完全不同的两个概念.引用mz3226960提出的MVC的概念,即M-model ...

  5. Spark累加器实现原理及基础编程

    Spark累加器实现原理及基础编程 实现原理 累加器用来把 Executor 端变量信息聚合到 Driver 端.在 Driver 程序中定义的变量,在Executor 端的每个 Task 都会得到这 ...

  6. Spark广播变量实现原理及基础编程

    Spark广播变量实现原理及基础编程 实现原理 广播变量用来高效分发较大的对象.向所有工作节点发送一个较大的只读值,以供一个或多个 Spark 操作使用.比如,如果你的应用需要向所有节点发送一个较大的 ...

  7. Servlet底层原理、Servlet实现方式、Servlet生命周期

    Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...

  8. Servlet 运行原理

    文章目录 Servlet 如何运行 演示 Servlet 运行原理 Servlet 如何运行 用户向浏览器地址栏输入:http://ip:port/helloweb/sayHello?name=zs ...

  9. Linux iptables:规则原理和基础

    什么是iptables? iptables是Linux下功能强大的应用层防火墙工具,但了解其规则原理和基础后,配置起来也非常简单. 什么是Netfilter? 说到iptables必然提到Netfil ...

  10. java启动servlet_Java Servlet 运行原理分析

    1 Servlet基本执行过程 Web容器(如Tomcat)判断当前请求是否第一次请求Servlet程序 . 如果是第一次,则Web容器执行以下任务: 加载Servlet类. 实例化Servlet类. ...

最新文章

  1. python函数手册 stata_Stata连享会-Python量化
  2. 从qplot开始入门
  3. QT 5 1 0 MinGW 的安装及使用
  4. Windows server 2008 R2 DHCP服务器的架设
  5. validation problems were found problem cvc-complex-type.2.4a
  6. Thinkphp在Nginx服务器下部署的问题--宝塔面板篇
  7. 智慧零售四年,来酷科技好大一盘棋
  8. 悉尼科技大学量子计算_世界排名前12位的量子计算研究型大学
  9. Android APP登录界面
  10. “FCoE全解系列”之网络融合交换机类型
  11. uni-app设置页面背景及背景图片
  12. 谷歌浏览器Chrome通过命令截图整个网页,screen,通过插件生成gif图片,以及通过插件进行录屏
  13. IOS6区别于IOS5的几个不明显的改变
  14. 如何生成一个二维码?
  15. string+DFS leetcode-17.电话号码下的字母组合
  16. 计算机网络应用技术技能大赛
  17. druid 配置理解及监控界面参数理解
  18. nokia x android 界面,诺基亚(NOKIA)X手机主界面评测-ZOL中关村在线
  19. K-means聚类实现图片分割
  20. 全球土地利用数据ESRI 10m Land Cover 2020 in GEE(Google Earth Engine)

热门文章

  1. 非线性光纤光学——光孤子
  2. 【音视频数据数据处理 2】【YUV篇】将YUV420P_I420数据旋转90°-180°-270°-镜像旋转
  3. 通讯录 按中文名字 拼音首字母 排序
  4. 计算机主机并行接口,计算机并行接口技术原理.ppt
  5. “我培训完JAVA,进了美团,美团氛围特别好,就是送餐特别累”
  6. 通达OA 商务平台OA2017新版本简易评测(图文)
  7. 通达OA2017版工作流触发器应用实例
  8. 通俗地讲解傅立叶分析和小波分析间的关系
  9. matlab多久可以入门,5分钟入门matlab
  10. clickhouse