【学习笔记】手写 Tomcat 四

news2024/11/15 5:55:48

目录

一、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 数据访问层

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2163528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业新购买的设备需要做仪器校准吗?为什么新设备也要做检测?

企业在定制一批设备仪器到厂后&#xff0c;会需要对设备进行验货和接收&#xff0c;而这个环节很多人对于设备是否同时需要先做校准的问题犹豫不决&#xff0c;一些企业觉得新设备都是刚买的&#xff0c;做仪器校准没有必要&#xff0c;但是另一部分认为即便是新买的仪器也要先…

easyexcel常见问题分析

文章目录 一、读取数字多了很多小数位的精度问题 一、读取数字多了很多小数位的精度问题 浮点型转成BigDecimal的时候会出现精度问题&#xff0c;例如 这儿设置的实体类对象类型是String&#xff0c;默认用到的是StringNumberConverter转换器 2.1.4 版本 public class Strin…

葡萄城亮相2024全球产品经理大会,共探创新之旅

葡萄城亮相2024全球产品经理大会&#xff0c;共探创新之旅 9 月 20-21 日&#xff0c;由 CSDN 举办的“2024 全球产品经理大会”于北京盛大开幕&#xff0c;现场超 40 位互联网与科技产品领域专业人士发表主题演讲&#xff0c;1000 与会者齐聚一堂。 大会现场气氛热烈&#…

【LangChain系列】实战案例5:用LangChain实现灵活的Agents+RAG,该查时查,不该查时就别查

目前为止&#xff0c;我们实现的RAG练习中&#xff0c;答案都是全部来源于检索到的文本内容。而检索过程可能在某些情况下是不需要的。 如何优化这个过程&#xff0c;让我们的RAG程序在必要时才去检索&#xff0c;不必要时&#xff0c;直接使用大模型原有数据来回答呢&#xf…

Python酷玩之旅_如何在Centos8顺利安装Python最新版(3.12)

全文导览 前言Q&#xff1a;如何在Centos8顺利安装Python最新版一. 下载安装包1.1 wget1.2. 官网下载 二. 执行安装2.1. 检查环境2.2. 安装依赖2.3. 解压tgz包2.4. 编译2.5. 安装2.6. 设置环境变量2.6.1 编辑/etc/profile2.6.2 激活生效 三. 操作示例3.1. helloworld 结语 前言…

DevOps学习路线图

DevOps 是软件工程领域中的一种文化和实践方法&#xff0c;它将开发 (Dev) 和运维 (Ops) 相结合&#xff0c;从而在应用程序规划、开发、交付和运营中统一人员、流程和技术。 DevOps 支持以前孤立角色&#xff08;如开发、IT 运营、质量工程和安全&#xff09;之间的协调和协作…

【Elasticsearch】-实现图片向量相似检索

1、http请求方式 如果elasticsearch服务设置账号密码&#xff0c;则在请求的header中添加 Basic Auth 认证 请求方式&#xff1a;Post 请求地址&#xff1a;/index_name/_search 请求body&#xff1a;json格式 {"size": 10, //返回条数"min_score": 0.…

【10000字pandas数据可视化超全笔记整理】Numpy Pandas | 常用API介绍 | 缺失值处理 matplotlib数据可视化介绍

文章目录 Numpy 部分NumPy的数组类被称作ndarray&#xff0c;通常被称作数组。属性创建方法内置函数运算基本运算矩阵运算 Pandas部分总述Serise 对象创建属性方法运算 DataFrame属性索引操作添加删除插入列 保存读取文件保存读取 数据加载分组聚合语法格式: 基本绘图 数据组合…

【设计模式-迭代】

定义 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;用于提供一种顺序访问集合对象元素的方式&#xff0c;而不暴露该对象的内部表示。通过迭代器&#xff0c;客户端可以在不需要了解集合实现的细节的情况下遍历集合中的元素。 UML图 …

Hi.Events —— 您的全方位活动管理与票务平台

大家好&#xff01;今天给大家介绍一个超厉害的开源项目&#xff1a;Hi.Events&#xff0c;这是一个功能丰富的自托管活动管理和票务平台&#xff0c;无论是会议还是俱乐部活动&#xff0c;它都能帮你轻松搞定&#xff01; 项目介绍 Hi.Events是一款功能丰富、自托管的开源活动…

用AI绘画年入百万?揭秘高效起始号与现变路径...

part.1 AI绘画的优势 AI绘画的魅力在于其高效和灵活 无论你是通过Midjourney还是Stable Diffusion&#xff0c;只需输入简单的提示词&#xff0c;再加上一些额外的控制调整&#xff0c;AI绘画就能快速生成各种创意内容。角色IP设计、游戏原画、3D场景甚至是天马行空的创意都…

服务器桌面监控录制到NVR

目录 一、硬盘录像机配置 二、配置VLC Play 三、加入NVR通道 四、实时画面 五、历史画面录像 有一天,客户提出一个问题,有一台服务器被很多人使用,成了共用服务器。共用的东西有个特点,就是人人都用,人人都不管,都认为自己只是偶尔用一下,管理维护不是自己的责任。…

Error: one input ui-file must be specified(问题已解决)

一、 项目场景问题描述 Error: one input ui-file must be specified pycharm IDE添加了外部工具。 QT Designer设计完成&#xff0c;生成界面ui文件&#xff0c; 3.运行pyuic5转换文件 方式一&#xff1a;选中ui文件 方式二:右击选中.ui文件。 报错&#xff1a;Error: o…

【xhs截流软件】爬取小红书关键词笔记下的筛选评论

用python开发的xhs采集工具【爬小红书搜索评论软件】&#xff0c;可用于引流截流等。 支持2种模式的评论采集&#xff1a; 根据关键词采集评论&#xff0c;爬取思路&#xff1a;笔记关键词->笔记链接->评论根据笔记链接采集评论&#xff0c;爬取思路&#xff1a;笔记链接…

k8s中,pod生命周期,初始化容器,容器探针,事件处理函数,理解其设计思路及作用

k8s中&#xff0c;为什么要设计pod 平台直接管理容器不是挺好的吗 为什么要以pod为单位进行管理&#xff0c; 然后把容器放在pod里面 那么有pod和没pod的区别是什么 也就是pod提供了什么作用 这个可以考虑从pod生命周期管理的角度去思考 如图&#xff0c;pod主容器在运行…

DataX实战:从MongoDB到MySQL的数据迁移--修改源码并测试打包

在现代数据驱动的业务环境中&#xff0c;数据迁移和集成是常见的需求。DataX&#xff0c;作为阿里云开源的数据集成工具&#xff0c;提供了强大的数据同步能力&#xff0c;支持多种数据源和目标端。本文将介绍如何使用DataX将数据从MongoDB迁移到MySQL。 环境准备 安装MongoDB…

从0新建一个微信小程序实现一个简单跳转

首先 1.从这里下载开发工具 https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/getstart.htm 2. 等下载完毕后 创建一个空白项目 在pages目录下右键创建一个page : testUI,这时候会生成四个文件 新建一个文件夹 testUI 给他们放一起 3.增加一个按钮 …

实战OpenCV之图像滤波

基础入门 图像滤波是数字图像处理中一种非常重要的技术&#xff0c;主要用于图像噪声去除、图像平滑、突出图像特征&#xff0c;或者进行图像风格的转换。它通过数学运算对图像中的像素值进行修改&#xff0c;以达到特定的处理目的。图像滤波可以分为两大类&#xff0c;分别为&…

matlab划分区域的等高线云图(代码)

出图结果如下&#xff1a; 代码如下&#xff0c;按需修改 clear;clc; numRows100; %数据区域&#xff0c;步长&#xff0c;步数 numCols100; ax-2;bx2; ay-2;by2; hx(bx-ax)/numCols; hy(by-ay)/numRows; XXCax:hx:bx; %坐标阵&#xff0c;data Y…

Ubuntu磁盘不足扩容

1.问题 Ubuntu磁盘不足扩容 2.解决方法 安装一下 sudo apt-get install gpartedsudo gparted