【JavaEE初阶】Servlet (三)MessageWall
在我们之前博客中写到的留言墙页面,有很严重的问题:(留言墙博客)
- 如果刷新页面/关闭页面重开,之前输入的消息就不见了.
- 如果一个机器上输入了数据,第二个机器上是看不到的.
针对以上问题,我们的解决思如如下:
让服务器来存储用户提交的数据,由服务器保存.
当有新的浏览器打开页面的时候,从服务器获取数据.
此时服务器就可以用来存档和读档.
设计程序:
写web程序,务必要重点考虑前后端如何交互,约定好前后端交互的数据格式.
设计前后端交互接口: 1.请求是什么样 2. 响应是什么样 3.浏览器什么时候发送这个请求 4. 浏览器按照什么格式来解析
在我们的留言墙程序中,以下环节涉及到前后端交互:
- 点击提交,浏览器把表白墙信息发送到服务器这里
- 页面加载,浏览器从服务器获取到表白信息.
- 点击提交,浏览器把表白墙信息发送到服务器这里
请求:
POST/message
按照json格式:
{from:"i",to:"you",message:"hello"
}
响应:
HTTP/1.1 200 OK
- 页面加载,浏览器从服务器获取到表白信息.
请求:
GET/message
响应:
HTTP/1.1 200 OK
body部分:
[{from:"i",to:"you",message:"hello"}{from:"i",to:"you",message:"hello"}
]
此处约定的目的是为了前端代码和后端代码能够对应上.约定的方式可以有很多种.
通过约定,我们可以写出后端代码为:
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{public String from;public String to;public String message;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {private List<Message> messageList = new ArrayList<>();ObjectMapper objectMapper = new ObjectMapper();// 向服务器提交数据@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//读取body中的内容,解析成 Message 对象Message message = objectMapper.readValue(req.getInputStream(),Message.class);//保存messageList.add(message);//设置状态码resp.setStatus(200);}// 从服务器获取数据@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//显示告诉浏览器,数据是 json 格式,字符集是 utf8resp.setContentType("application/json;charset=utf8");//通过 writeValue 将 messageList(java对象) 转成 json 格式并将其写入 resp 中//objectMapper.writeValue(resp.getWriter(),messageList);//把java对象转成json字符串String jsonResp = objectMapper.writeValueAsString(messageList);System.out.println("jsonResp: "+jsonResp);//把这个字符串写回到响应 body 中resp.getWriter().write(jsonResp);}
}
通过Postman 构造POST请求,使用json语法编辑body部分,点击两次发送,再通过GET获取得到响应如下:

存档:
其次,我们再看前端代码:在前端代码中使用ajax发送一个post请求.

通过fiddler得到:

通过dopost 执行:

通过resp.setStatus(200);回到回调函数:

读档:根据ajax创建GET响应:

打开fiddler可以看到:

GET请求触发doGet方法:

我们可以得到完整的前端代码(包含撤销功能):
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>MessageBoard</title><style>*{/* 消除浏览器的默认样式 */margin: 0;padding: 0;/* 保证盒子不会撑大 */box-sizing: border-box;/* background-color: rgba(255, 192, 203, 0.436); */}.container{width: 600px;margin: 20px auto;}h1 {text-align: center;}p {text-align: center;color: #666;margin: 20px 0;}.row {/* 开启弹性布局 */display: flex;height: 40px;/* 水平方向居中 */justify-content: center;/* 垂直方向居中 */align-items: center;}.row span {width: 80px;}.row input {width: 200px;height: 30px;}.row button {width: 280px;height: 30px;color: white;background-color: orange;/* 去掉边框 */border: none;border-radius: 5px;}/* 点击的时候有个反馈 */.row button:active {background-color: grey;}</style>
</head>
<body><div class="container"><h1>留言板</h1><p>输入内容后点击提交,信息会显示到下方表格中</p><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 class="row"><button id="revert">撤销</button></div></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script><script>//实现提交操作,点击提交,就能够吧用户输入的内容提交到页面上显示//点击时,获取到三个输入框的文本内容//创建一个新的div.rom把内容构造到这个div中即可.let containerDiv = document.querySelector('.container');let inputs = document.querySelectorAll('input');let button = document.querySelector('#submit');button.onclick = function(){//1.获取到三个输入框的内容let from = inputs[0].value;let to = inputs[1].value;let msg = inputs[2].value;if(from == '' || to == '' || msg == ''){return;}//2.构造新的divlet rowDiv = document.createElement('div');rowDiv.className = 'row message';rowDiv.innerHTML = from +" 对 "+to+' 说 : '+msg;containerDiv.appendChild(rowDiv);//3.清空输入聊天框for(let input of inputs){input.value = '';}//4. [新增] 给服务器发起post请求,把上述数据提交到服务器//定义一个js对象let body = {from:from,to:to,message:msg};//将对象转成json字符串strBody = JSON.stringify(body);$.ajax({type:'post',url:'message',data:strBody,contentType:"application/json; charset=utf-8",success:function(body){console.log("发布成功.");}});}let revertButton = document.querySelector('#revert');revertButton.onclick = function(){//删除最后一条消息//选中所有的row,找出最后一个row,然后删除let rows = document.querySelectorAll('.message');if (rows == null || rows.length == 0) {return;}containerDiv.removeChild(rows[rows.length - 1]);//[新增]删除$.ajax({type: 'delete',url: 'message',success: function(body){// 1.先选中父元素ConversationDiv,然后删除所有子元素let ConversationDiv = document.querySelector('.Conversation');while(ConversationDiv.firstChild){ConversationDiv.removeChild(ConversationDiv.firstChild)}for(let message of body){// 2.对响应数据内容进行页面显示,对每一个message元素构造一个divlet resultDiv = document.createElement('div');resultDiv.className = 'row message result';resultDiv.innerHTML = message.from + ' 对: ' + message.to + ' 说: ' + message.message;ConversationDiv.appendChild(resultDiv);}}})}//[新增]在页面加载的时候,发送GET请求,从服务器获取到数据并添加到页面中$.ajax({type:'get',url:'message',success:function(body){let containerDiv = document.querySelector('.container');for(let message of body){let rowDiv = document.createElement('div');rowDiv.className ='row message';rowDiv.innerHTML = message.from +' 对 '+ message.to +' 说 : ' + message.message;containerDiv.appendChild(rowDiv);}}});</script>
</body>
</html>

刷新页面后数据也不会消失.
但是以上重启服务器后数据就消失了,所以我们可以把数据写入数据库中进行长久的保存.
C:\Users\xxxflower>mysql -uroot -p
Enter password: ****
create table message(from varchar(20),to varchar(20),message varchar(1024));
注意,由于from和to都是sql中的关键字,所以需要使用`.
创建表:

完整的后端代码:
MessageServlet.java:
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;class Message{public String from;public String to;public String message;
}
@WebServlet("/message")
public class MessageServlet extends HttpServlet {//private List<Message> messageList = new ArrayList<>();private ObjectMapper objectMapper = new ObjectMapper();// 向服务器提交数据@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//读取body中的内容,解析成 Message 对象Message message = objectMapper.readValue(req.getInputStream(),Message.class);//保存save(message);//messageList.add(message);//设置状态码resp.setStatus(200);}// 从服务器获取数据@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//显示告诉浏览器,数据是 json 格式,字符集是 utf8resp.setContentType("application/json;charset=utf-8");//通过 writeValue 将 messageList(java对象) 转成 json 格式并将其写入 resp 中//objectMapper.writeValue(resp.getWriter(),messageList);//把java对象转成json字符串List<Message> messageList = load();String jsonResp = objectMapper.writeValueAsString(messageList);System.out.println("jsonResp: " + jsonResp);//把这个字符串写回到响应 body 中resp.getWriter().write(jsonResp);}//使用jdbc 往数据库里面存消息private void save(Message message) {//JDBCConnection connection = null;PreparedStatement statement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "insert into message values(?, ?, ?)";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) {throw new RuntimeException(e);}finally {//4.关闭连接DBUtil.close(connection,statement,null);}}//从数据库取所有消息private List<Message> load(){List<Message> messageList = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sqlString sql = "select * from message";statement = connection.prepareStatement(sql);//3.执行sqlresultSet = statement.executeQuery();//4.遍历集合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 e) {e.printStackTrace();}finally {//5.释放资源 断开连接DBUtil.close(connection,statement,resultSet);}return messageList;}@Overrideprotected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf-8");// 从数据库获取所有表白信息// 1.获取最后一份表白信息,并从数据库删除List<Message> messageList = load();if(messageList.size() == 0){return;}Message messageEnd = messageList.get(messageList.size()-1);int count = delete(messageEnd);if(count == 1){System.out.println("留言信息删除成功");}else {System.out.println("留言信息删除失败");}// 2.获取删除最后一条信息后的 全部表白信息,并写回到浏览器messageList = load();objectMapper.writeValue(resp.getOutputStream(), messageList);}private int delete(Message message) {Connection connection = null;PreparedStatement preparedStatement = null;int count = 0;try {// 1.获取连接connection = DBUtil.getConnection();// 2.编写sqlString sql = "delete from message where `from` = ? and `to` = ? and message = ?";// 3.获取预编译对象,进行预编译preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, message.from);preparedStatement.setString(2, message.to);preparedStatement.setString(3, message.message);// 4.执行sql语句count = preparedStatement.executeUpdate();// 5.处理结果} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.close(connection, preparedStatement, null);}return count;}
}
DBUtil:
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//这个类用于封装数据库连接过程
//此处把 DBUtil 作为一个工具类,提供 static 方法供其他代码使用
public class DBUtil {//静态成员跟随类对象,类对象在整个进程中只有唯一一份//静态成员相当于也是唯一的实例(单例模式,饿汉模式)private static DataSource dataSource = new MysqlDataSource();//使用静态代码块针对 DataDource 进行初始化操作static {((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javaee?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("0828");}//建立连接public static Connection getConnection() throws SQLException {return dataSource.getConnection();}//断开连接 释放资源public static 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();}}}
}
最终实现效果:

相关文章:
【JavaEE初阶】Servlet (三)MessageWall
在我们之前博客中写到的留言墙页面,有很严重的问题:(留言墙博客) 如果刷新页面/关闭页面重开,之前输入的消息就不见了.如果一个机器上输入了数据,第二个机器上是看不到的. 针对以上问题,我们的解决思如如下: 让服务器来存储用户提交的数据,由服务器保存. 当有新的浏览器打开页…...
D. Make It Round
在Berlandia发生了通货膨胀,所以商店需要改变商品的价格。 商品n的当前价格已经给出。允许将该商品的价格提高k倍,1≤k≤m,k为整数。输出商品的最圆的可能的新价格。也就是在最后有最大数量的零的那个。 例如,数字481000比数字1…...
Python网站页面开发HTML总结
Python网站页面开发HTML总结 一、HTML基础语法 1.HTML是什么? ●HTML是HyperText Mark-up Language的首字母简写,即超文本标记语言。 ●HTML不是一种编程语言,而是一种标记语言。 ●超文本指的是超链接,标记指的是标签…...
[个人笔记] vCenter设置时区和NTP同步
VMware虚拟化 - 运维篇 第三章 vCenter设置时区和NTP同步 VMware虚拟化 - 运维篇系列文章回顾vCenter设置时区和NTP同步(附加)ESXi设置alias参考链接 系列文章回顾 第一章 vCenter给虚机添加RDM磁盘 第二章 vCenter回收活跃虚拟机的剩余可用空间 vCente…...
(原创)Flutter与Native通信的方式:EventChannel和BasicMessageChannel
前言 上一篇博客主要介绍了MethodChannel的使用方式 Flutter与Native通信的方式:MethodChannel 这篇博客接着讲另外两种通信方式 EventChannel和BasicMessageChannel EventChannel用于从native向flutter发送通知事件,例如flutter通过其监听Android的重…...
【解决】el-tree报Cannot read property ‘getCheckedKeys‘ of undefined
如果你报错 Cannot read property getCheckedKeys of undefined 或者 Cannot read property getCheckedNodes of undefined 只要在你的在<el-tree>上加个这个,就可以了 ref"tree"...
车载软件架构 —— 信息安全与基础软件
车载软件架构 —— 信息安全与基础软件 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕…...
C\C++内存管理
目录 1.C/C内存分布2.C语言中动态内存管理方式3.C中动态内存管理3.1new/delete内置类型3.2new和delete操作自定义类型 4.operator new与operator delete函数4.2重载operator new与operator delete(了解) 5.new和delete的实现原理5.1内置类型5.2 自定义类…...
会议室预约系统-检验是否被预约核心SQL
会议室预约时,判断能否被预约,即查询是否已经有预约记录,存在不能被预约。 s,e;表示已经预约的开始结束时间; ns,ne,表示表单提交的预约时间; 只需要(ns,ne)与(s,e)区间没有交集,可…...
C++11类模板
类模板是用来生成类的蓝图,与函数模板的不同之处是,编译器不能为类模板推断模板参数类型。 所以我们在使用类的时候要带上<>并且指定类型如下 vector<int> v; // 需要带上<int> 哦定义类模板 如下,和函数模板差不多都是…...
SpiderFlow爬虫平台(爬虫学习)
申明 作为自己学习的记录,方面后期查阅 官网 SpiderFlow官网 简介 spider-flow 是一个爬虫平台,以图形化方式定义爬虫流程,无需代码即可实现一个爬虫 是使用springboot开发的项目,后端代码直接运行即可使用...
Rime输入法配置
Rime输入法在我电脑上,删了装,装了删,已经反复好几次了。就像是Vim,用它的时候,感觉各种配置太麻烦,想要的功能不知道怎么实现。转用其它编辑器的时候,却又念着它的快捷键和可定制性,…...
R语言学习笔记--列表list、数据框
列表 1-列表 列表可以包含不同类型的对象,也就是说,列表不是将某些具体的值组织起来,而是组织R对象。列表将数据组织在一个一维集合中。 列表非常好用,因为它可以装任何类型的对象,不要求数据之间是同质的。 创建列…...
电磁波定义、特性以及信道相关知识
文章目录 前言一、电磁波的定义、特性、波谱1、电磁波的特性2、电磁波谱的划分及用途 二、地球大气层的结构三、电磁波的传播方式1、地波(ground-wave)2、天波(sky-wave)3、视线传播(line-of-sight)①、相关…...
TCP KeepAlive与HTTP Keep-Alive
TCP KeepAlive与HTTP Keep-Alive TCP KeepAliveHTTP Keep-AliveTCP服务器怎么检测客户端断开连接 TCP KeepAlive TCP连接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,那么TCP需要判断是应用程序掉线了还…...
SkyWalking链路追踪-Agent (代理人)
基础概念: SkyWalking链路追踪代理(SkyWalking Tracing Agent)是一种用于收集和传输链路追踪数据的工具。它与应用程序一起部署,并通过自动或手动方式来收集关于应用程序中的请求路径和操作的信息。该代理将收集到的数据发送到Sky…...
多线程案例 | 单例模式、阻塞队列、定时器、线程池
多线程案例 1、案例一:线程安全的单例模式 单例模式 单例模式是设计模式的一种 什么是设计模式? 设计模式好比象棋中的 “棋谱”,红方当头炮,黑方马来跳,针对红方的一些走法,黑方应招的时候有一些固定的…...
C++文件操作
1.写文件 //文件操作 #include<fstream> int main() {//写文件//路径 -- 此路径没有就生成给文件 string filePath R"(E:\项目\test.txt)";//打开文件 ios::app在后面追加内容 啥也不跟是覆盖写入ofstream fout(filePath, ios::app);//检查是否打开成功if (…...
overleaf(latex) 公式过大,需要调小字体,同时公式编号字体不变的方法
提问:用latex编辑的双列排版的论文中,如果一个包含矩阵的公式中,矩阵过大,导致超出列的范围,一般该如何调整呢? 回答:如果你在LaTeX中的双列排版中遇到了一个矩阵过大而导致超出列范围的问题&a…...
flink采用thrift读取tablets一个天坑
原先的配置 [INFO] StarRocksSourceBeReader [open Scan params.mem_limit 8589934592 B] [INFO] StarRocksSourceBeReader [open Scan params.query-timeout-s 600 s] [INFO] StarRocksSourceBeReader [open Scan params.keep-alive-min 100 min] [INFO] StarRocksSourceBeRea…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
