【学习笔记】手写 Tomcat 四
目录
一、Read 方法返回 -1 的问题
二、JDBC 优化
1. 创建配置文件
2. 创建工具类
3. 简化 JDBC 的步骤
三、修改密码
优化返回数据
创建修改密码的页面
注意
测试
四、优化响应动态资源
1. 创建 LoginServlet 类
2. 把登录功能的代码放到 LoginServlet 类
3. 创建LoginServlet 对象,调用service方法
五、作业
1. 每个 servlet 的 service 方法都是一样的,如何优化?
2. 如何再进一步优化 Servlet
一、Read 方法返回 -1 的问题
在 上次的基础上,我们需要解决一下 read 读取到 -1 导致报错的问题
我们需要知道,为什么会读取到 -1?什么情况下会读取到 -1
read 方法返回 -1 有以下几种情况
1. 客户端 Socket 关闭(Socket.close)
2. 客户端关闭输出流,客户端在关流的时候,还多了一个往服务器写结束标记的动作,结束标记 -1
3. 读取超时抛出异常,java 中的 Socket 默认没有超时限制
4. 读取文件时,到了文件的末尾,表示没有数据可读,也会返回 -1
那么如何解决呢?
很简单,判断读取到的是不是 -1 ,如果是 -1 直接 return 不用往下执行了

二、JDBC 优化
当前数据库连接信息都写在了代码里面,相对于硬编码,如果以后连接信息更改了,比如数据库连接地址,数据库密码等,那么所有关于JDBC 获取数据库连接的代码都需要修改
解决方案:
生成配置文件
通过Properties类去解析配置文件
创建一个DBPropUtils工具类来获取配置文件的信息
1. 创建配置文件
配置数据库连接信息

2. 创建工具类
获取配置文件的数据库连接信息

package com.shao.Utils;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;public class DBPropUtil {private static HashMap<String, String> propMap = new HashMap<String, String>();static {Properties prop = new Properties();try {// 读取数据库配置文件// config 包名// DBConnection.properties 配置文件名prop.load(new FileInputStream("config" + File.separator + "DBConnection.properties"));// 获取所有的键Set<Object> keySet = prop.keySet();Iterator<Object> it = keySet.iterator();// 遍历 键的 Set 集合while (it.hasNext()) {// 把键转成字符串类型String key = (String) it.next();// 获取键对应的值String value = prop.getProperty(key);// 把键值对 添加到 map集合中propMap.put(key, value);}} catch (IOException e) {throw new RuntimeException(e);}}// 对外提供一个接口,通过key 获取对应的值public static String getProp(String key) {return propMap.get(key);}
}
3. 简化 JDBC 的步骤
创建 数据库连接类 获取连接,释放资源的方法也可以放到这个类里,这样可以简化响应类的代码

package com.shao.Utils;import java.sql.*;public class DBConnectUtil {/*** 静态代码块,当类被加载时,就会执行代码块,且只执行一次* */static {try {// 加载驱动Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 对外提供的方法,获取数据库连接*/public static Connection getConnection() {Connection connection = null;try {connection = DriverManager.getConnection(DBPropUtil.getProp("url"),DBPropUtil.getProp("user"),DBPropUtil.getProp("password"));} catch (SQLException e) {e.printStackTrace();}return connection;}/*** 释放资源*/public static void releaseSource(Connection connection, PreparedStatement pstmt, ResultSet resultSet) {// 释放连接try {if (connection != null) {connection.close();}} catch (SQLException e) {e.printStackTrace();}//关闭预编译对象try {if (pstmt != null) {pstmt.close();}} catch (SQLException e) {e.printStackTrace();}//关闭结果集try {if (resultSet != null) {resultSet.close();}} catch (SQLException e) {e.printStackTrace();}}
}
在响应类中调用方法获取数据库连接

测试

三、修改密码
登录功能有了,我们再练习一个修改密码的功能
和登录功能类似,因为我们封装了 JDBC 的获取数据库连接的操作,简化了JDBC,所以获取连接只需要调用工具类的方法就可以了


Connection connection = null;PreparedStatement pstmt = null;int result = 0;try {// 3. 获取数据库连接connection = DBConnectUtil.getConnection();// 4. 获取可执行对象// 定义 SQL 语句String SQL = "UPDATE train.users SET password = ? WHERE account = ?";pstmt = connection.prepareStatement(SQL);// 设置占位符的值String account = httpRequest.getRequestBodyParams().get("account");String password = httpRequest.getRequestBodyParams().get("password");pstmt.setString(1, password);pstmt.setString(2, account);// 5. 执行sql语句,获取结果result = pstmt.executeUpdate();// 6. 结果处理responseDTO responseDTO = null;if (result > 0) {responseDTO = new responseDTO(200, null, "修改成功");} else {responseDTO = new responseDTO(201, null, "修改失败");}// 调用方法返回数据send(JSON.toJSONBytes(responseDTO));} catch (SQLException e) {e.printStackTrace();} finally {// 7. 释放资源// 因为 更新,添加,删除 不需要结果集,所以不需要 resultSet,不用释放资源DBConnectUtil.releaseSource(connection, pstmt, null);}
优化返回数据
我们现在返回的数据是字符串类型的,然后转成字节数组,这样的话,数据响应到客户端还是字符串格式,不方便解析数据,所以需要把要响应的数据转成 JSON 格式
如何转成 JSON 格式呢?
1. 添加第三方 jar 包
百度网盘 fastjson2

2. 使用
转成 JSON 格式,然后转成字节数组,因为可以直接转成字节数组,为了方便,我们把响应方法里接收数据的参数也改成字节数组类型


创建修改密码的页面
百度网盘 jquery 文件

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>修改密码</title><script src="../static/js/jquery-3.5.1.min.js"></script>
</head>
<body>
<div><div class="content"><input type="text" class="text-input" name="account" placeholder="请输入账号"><input type="text" class="text-input" name="password" placeholder="请输入新密码"><button type="button" class="submit-button">修改密码</button></div><div class="msg"><span class="success-msg"></span></div></div>
</body>
<style>.content {margin: 0 auto;width: 300px;}.msg {margin: 0 auto;width: 300px;}.text-input {width: 200px;height: 30px;margin: 10px;padding: 5px;border: 1px solid #ccc;}.submit-button {width: 100px;height: 30px;margin: 10px;padding: 5px;border: 1px solid #ccc;cursor: pointer;border-radius: 5px;box-shadow: 0 0 1px #ccc;}
</style>
<script>document.querySelector('.submit-button').onclick = function () {console.log("点击了修改按钮")let username = document.querySelector('input[name="account"]').value;let password = document.querySelector('input[name="password"]').value;let data = {account: username,password: password,}$.ajax({url: 'http://127.0.0.1:8080/ChangePassword',type: 'POST',data: data,success: function (data) {console.log(data)data = JSON.parse(data)if (data.statusCode === 200) {document.querySelector('.success-msg').innerHTML = data.msg;} else {document.querySelector('.success-msg').innerHTML = data.msg;}}})}</script>
</html>
注意 ❗❗❗
因为修改密码的页面有引用到 static 文件夹下的 js 文件夹的 jquery 文件,当页面在游览器打开后,会自动请求 jquery 文件,当请求到达后端时,我们给请求资源的路径加了 webs/pages 的前缀,这样的话,就会在 webs/pages/static/js 文件夹下找 jquery 文件,路径不对,所以获取不到

解决方案是,只保留 webs/ 前缀即可,这样资源的路径就正确了

测试
在游览器的地址栏中输入 http://127.0.0.1:8080/pages/changePassword.html
因为删除了 pages 前缀,所以请求 pages 文件夹下的资源文件都需要加上 pages

四、优化响应动态资源
现在的结构是响应动态资源的代码都写在了响应类里,这样的话,当后面加了很多功能,响应类的代码就会很多,一是不好维护,二是不利于协同开发
那怎么解决呢?
解决方案是一个功能做成一个 servlet
1. 创建 LoginServlet 类

2. 把登录功能的代码放到 LoginServlet 类
package com.shao.Servlet;import com.alibaba.fastjson2.JSON;
import com.shao.Utils.DBConnectUtil;
import com.shao.Utils.responseDTO;
import com.shao.net.HttpRequest;
import com.shao.net.HttpResponse;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class LoginServlet {Connection connection = null;PreparedStatement pstmt = null;ResultSet resultSet = null;responseDTO responseDTO = null;public void service(HttpRequest request, HttpResponse response) {if (request.getRequestMethod().equals("GET")) {doGet(request, response);} else if (request.getRequestMethod().equals("POST")) {doPost(request, response);}}public void doGet(HttpRequest request, HttpResponse response) {try {// 3. 获取数据库连接connection = DBConnectUtil.getConnection();// 4. 获取可执行对象String SQL = "select count(*) from train.users where account = ? and password = ?";pstmt = connection.prepareStatement(SQL);// 设置占位符的值String account = request.getRequestBodyParams().get("account");String pwd = request.getRequestBodyParams().get("password");pstmt.setString(1, account);pstmt.setString(2, pwd);// 5. 执行sql语句,获取结果集resultSet = pstmt.executeQuery();// 6. 结果处理if (resultSet.next() && resultSet.getInt(1) > 0) {responseDTO = new responseDTO(200, null, "登录成功");} else {responseDTO = new responseDTO(201, null, "登录失败,请检查账号和密码");}//调用方法返回数据response.send(JSON.toJSONBytes(responseDTO));} catch (Exception e) {e.printStackTrace();} finally {// 7. 释放资源DBConnectUtil.releaseSource(connection, pstmt, resultSet);}}public void doPost(HttpRequest request, HttpResponse response) {responseDTO = new responseDTO(400, null, "不支持POST提交方法");response.send(JSON.toJSONBytes(responseDTO));}
}
3. 创建LoginServlet 对象,调用service方法

五、作业
1. 每个 servlet 的 service 方法都是一样的,如何优化?
2. 如何再进一步优化 Servlet
把Servlet 进一步划分为 Servlet 层,Service 业务逻辑层和 Dao 数据访问层
相关文章:
【学习笔记】手写 Tomcat 四
目录 一、Read 方法返回 -1 的问题 二、JDBC 优化 1. 创建配置文件 2. 创建工具类 3. 简化 JDBC 的步骤 三、修改密码 优化返回数据 创建修改密码的页面 注意 测试 四、优化响应动态资源 1. 创建 LoginServlet 类 2. 把登录功能的代码放到 LoginServlet 类 3. 创…...
探索基因奥秘:汇智生物如何利用组蛋白甲基化修饰测序技术革新农业植物基因组研究?
引言: 随着生物医学技术的不断进步,我们对生命奥秘的探索越来越深入。在众多的生物技术中,表观组学分析技术逐渐成为研究的热点。本文将带您走进汇智生物,了解他们如何利用DNA亲和纯化测序技术(DAP-seq)推…...
二叉搜索树的介绍、模拟实现二叉搜索树、leetcode---根据二叉树创建字符串、leetcode---二叉树的最近公共祖先等的介绍
文章目录 前言一、二叉搜索树的介绍二、模拟实现二叉搜索树三、leetcode---根据二叉树创建字符串四、leetcode---二叉树的最近公共祖先总结 前言 二叉搜索树的介绍、模拟实现二叉搜索树、leetcode—根据二叉树创建字符串、leetcode—二叉树的最近公共祖先等的介绍 一、二叉搜索…...
人工智能的基本概念与发展历程
一、人工智能的基本概念与发展历程 人工智能是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。它涵盖了机器人技术、语言识别、图像识别、自然语言处理和专家系统等众多领域。自20世纪30年代数理逻辑的形式化和智能可计算思想开始构建计…...
【IPV6从入门到起飞】5-6 IPV6+Home Assistant(ESPHome+ESP-cam)实时监控
5-6 IPV6Home Assistant[ESPHomeESP-cam]实时监控 1、背景2、ESPHome 安装2-1 ESPHome 简述2-2 安装 3、创建ESP32-CAM设备4、编辑yaml配置4-1 找到合适的配置4-2 修改配置4-3 验证配置4-4 编译项目 5、烧录固件6、绑定设备7、效果实现 1、背景 在前面我们已经实现了数据采集与…...
生成式AI的未来
随着生成式AI技术的不断进步,关于其未来发展方向的讨论也愈发激烈。究竟生成式AI的未来是在对话系统(Chat)中展现智慧,还是在自主代理(Agent)中体现能力?这一问题不仅涉及技术实现的可能性&…...
实用好软-----电脑端 从视频中导出音频的方便工具
最近想从一个视频中导出个音乐,百度找很多没有合适的工具。最终找到了一款很方便 而且操作超级简单的工具。打开这个工具后只需要把需要导出音乐的视频拖进窗口里就会自动导出音乐mp3。方便小巧,而且音频效果还是不错的。 一些视频转换成音频文件&#x…...
3-基于容器安装carla
用户可以将基于CARLA发布的镜像拉到Docker容器中运行。这对于以下用户很有用: 想要运行CARLA而不需要安装所有依赖项 运行多台CARLA服务器,进行GPU映射。 运行不显示的CARLA服务器 本节解释了运行CARLA图像的要求,以及如何使用OpenGL和Vulkan图形api运行…...
循环程序结构课堂练习题解
A 如果药够, 则拿药, 否则记录 #include <stdio.h>int main() {int m, n, i;scanf("%d", &m);scanf("%d", &n);int ans 0;for(i 1; i < n; i ){int temp;scanf("%d", &temp);if(m > temp){m - temp;}else{ans ;}}p…...
SpringBoot搭建
第一种创建方式 第二种创建方式 第三种创建 第四种手动创建 最后把controller写好...
【ChatGPT】Python 实现计算两线段的变换矩阵
作为一个数学专家,请给出下面的这个问题的数学解法; 要求如下: 1. 给出数学推理公式 2. 给出 python 的实现方式已知条件: 1. 三维空间中,线段L1,L1 由点 A1 (ax1, ay1, az1) 与 B1 (bx1, by1, bz1) 组成&a…...
大数据Hologres(二):Hologres 快速入门
文章目录 Hologres 快速入门 一、资源领取 二、入门体验 1、创建数据库 2、创建表 3、导入示例数据 4、查询表中数据 Hologres 快速入门 一、资源领取 领取链接: 阿里云免费试用 - 阿里云 (aliyun.com) 二、入门体验 1、创建数据库 进入Hologres管理控制…...
华为仓颉语言入门(7):深入理解 do-while 循环及其应用
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 用法说明 do-while 表达式是一种控制循环的结构,它允许代码在每次循环之后进行条件判断。在这个表达式中,无论条件一开始是否满足,代码块都会被至少执行一次。 语法…...
在传销案件中数据库取证的分步指南
金字塔计划的特点是分层结构,主要由招募新成员的机制驱动。取证部门调查这些方案时,往往依靠数据库记录来分析这种结构。这些记录详细描述了上级和下级之间的关系,使调查人员能够描绘出组织的动态。在本文中,我们将探讨如何利用数…...
数据结构与算法——Java实现 21.栈
目录 一、概述 二、基于链表的栈的实现 接口 链表接口实现类 测试类 编辑 三、基于数组的栈的实现 接口 数组接口实现类 测试类 妈妈,生日快乐,希望你健康快乐没有烦恼也不会有病痛 —— 24.9.28 一、概述 计算机科学中,stack是一种线性的…...
实验一 网络基础及仿真模拟软件Packet Tracer 入门
实验一 网络基础及仿真模拟软件Packet Tracer 入门 【实验目的】 一、认识 Packet Tracer 。 二、学习使用 Packet Tracer 进行拓扑的搭建。 三、学习使用 Packet Tracer 对设备进行配置,并进行简单的测试。 【实验内容和结果】 一、拖放设备和布置线缆 二、用…...
建立分支提交代码
git分支 git branch 产看当前分支 git branch -a 查看所有分支 git checkout 分支名 切换分支 git checkout -b 分支名 建立分支(仅仅是在本地建立了,并没有关联线上) git push --set-upstream origin 分支名 把本地分支推到先线上 gti add …...
认识 Linux操作系统
前言 电脑由硬件和软件相构成,在软件中操作系统只是其中的一个分支,今天我们学习的Linux有是操作系统中的一种,不同的操作系统有自己的特点和生存生态。市面上大多数电脑自带的操作系统都是我们熟知的Windows。Linux将会为大家带来开源的新天…...
AI时代程序员的核心竞争力提升与保持之道
一、引言 ---- 随着人工智能(AI)和生成式人工智能(AIGC)技术的迅速发展,包括chatgpt、midjourney、claude等大语言模型接连不断地涌现,AI辅助编程工具在程序员社区中的普及正在悄然改变我们的工作方式。…...
状态模式原理剖析
《状态模式原理剖析》 状态模式(State Pattern) 是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。换句话说,当对象状态发生变化时,它的行为也会随之变化。 通过状态模式,可以消除通过 if-else…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
