【学习笔记】手写Tomcat 四

news2025/1/17 21:57:51

目录

一、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/2154312.html

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

相关文章

hackmyvm靶场--zon

环境 攻击机kali 靶机 未知 主机探测 因为在同一个局域网内使用ARP协议探测存活主机 靶机为192.168.56.128 端口探测 常见的80和22端口 那么一定是寻找web漏洞拿shell了 后台扫描 后台扫描常用dirsearch和gobuster,有时候小字典可能不太行&#xff0c;可以尝试换个大点…

Android SystemUI组件(07)锁屏KeyguardViewMediator分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分即可。 为了更好理解本文的内容&#xff0c;优先说明下SystemUI中与Ke…

电力行业螺钉螺帽螺丝缺失检测数据集 voc yol

电力行业螺钉螺帽螺丝缺失检测数据集 数据集描述 该数据集旨在用于电力行业中的螺钉、螺帽、螺丝等紧固件的缺失检测任务。数据集包含了大量的图像及其对应的标注信息&#xff0c;可用于训练计算机视觉模型&#xff0c;以识别和定位电力设施中的螺钉、螺帽、螺丝等部件是否存在…

Go-知识-定时器

Go-知识-定时器 1. 介绍2. Timer使用场景2.1 设定超时时间2.2 延迟执行某个方法 3. Timer 对外接口3.1 创建定时器3.2 停止定时器3.3 重置定时器3.4 After3.5 AfterFunc 4. Timer 的实现原理4.1 Timer数据结构4.1.1 Timer4.1.2 runtimeTimer 4.2 Timer 实现原理4.2.1 创建Timer…

golang学习笔记1-go程序执行流程

声明&#xff1a;本人已有C&#xff0c;C,Python基础&#xff0c;只写本人认为的重点&#xff0c;方便自己回顾。 命令行执行go程序有两种方式&#xff0c;其流程如下图 注意第一种方式会得到可执行文件&#xff0c;第二种不会。 例1 在当前目录下编译hello.go go build hel…

更换硬盘后,电脑装完系统进不去?或PE能识别硬盘但开机/启动/BIOS识别不了硬盘解决办法

由于现在的电脑主板&#xff0c;默认都是UEFI启动&#xff0c;硬盘只有使用GUID分区表&#xff0c;主板BIOS才找得到系统引导&#xff01; 而当我们拿到一块新硬盘&#xff0c;使用分区工具默认类型分区&#xff0c;默认是MBR类型&#xff0c;所以这种分区的硬盘&#xff0c;B…

【后端开发】JavaEE初阶—线程的理解和编程实现

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解多线程的知识哟~~~&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;【后端开发】JavaEE初阶——计算机是如何工作的&#xff1f;&#xff1f;&#xff1f;-CSDN博客 &#x1f308;感兴趣的小伙…

腾讯云ssl证书到期,续期免费证书并部署

首先咱用的免费证书&#xff0c;现在最长90天有效期&#xff0c;今天又到期了&#xff0c;得及时续期避免关联资源访问受限。 我这个证书是关联了一个负载均衡和两个容器服务的&#xff08;如果您的应用是ngnix或其他的应用那和本文操作可能有点不一样&#xff09; 所以需要做…

17【Protues单片机仿真】基于51单片机的太阳能智能谷物翻晒机器人

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;避障&#xff0c;低于50CM报警&#xff0c;LED灯亮起&#xff0c;自动翻晒用光敏电阻&#xff0c;光照强度大&#xff0c;电机转动&#xff0c;相当于翻晒粮食&#xff0…

【python设计模式7】行为型模式2

目录 策略模式 模板方法模式 策略模式 定义一个个算法&#xff0c;把它们封装起来&#xff0c;并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。角色有&#xff1a;抽象策略、具体策略和上下文。 from abc import abstractmethod, ABCMeta from datetim…

2024年最新Redis内存数据库主从复制、哨兵模式、集群部署等详细教程(更新中)

Centos 安装 Redis 检查安装 GCC 环境 [rootVM-4-17-centos ~]# gcc --version gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; no…

数据库(选择题)

基本概念 数据库&#xff08;DB&#xff09;&#xff1a;长期存储在计算机内的、有组织的、可共享的数据集合。 数据库管理系统&#xff08;DBMS&#xff09;&#xff1a;它是数据库的机构&#xff0c;是一个系统软件&#xff0c;负责数据库中的数据组织、数据操纵、数据维护…

深度学习03-神经网络02-激活函数

可以使用这个进行跳转链接​​​​​​​http://playground.tensorflow.org/#activationrelu&batchSize11&datasetspiralDatasetreg-gauss&learningRate0.01ularizationRate0.1&noise0&networkShape7,5,4,3,2&seed0.54477&showTestDatafalse&d…

C语言常见字符串函数模拟实现一

strlen模拟实现 重点&#xff1a;1.字符串已经\0作为结束标志&#xff0c;strlen返回的是字符串\0前面出现的字符个数&#xff08;不包含\0&#xff09; 2.参数指向的字符串必须要以\0结束。 3.注意函数的返回值是size_t&#xff0c;是无符号的&#xff0c;加减是无法对比的。…

nginx模块篇(四)

文章目录 四、Nginx的扩展模块4.1. Lua4.1.1 概念4.1.2 特性4.1.3 应用场景4.1.4 Lua的安装4.1.5 Lua的语法4.1.5.1 第一个Lua程序4.1.5.2 Lua的注释4.1.5.3 标识符4.1.5.4 关键字4.1.5.5 运算符4.1.5.6 全局变量&局部变量4.1.5.7 Lua数据类型nilbooleannumberstringtablef…

LeetCode[中等]

给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 思路&#xff1a; 计算链表长度num&#xff0c;num - n就是需要删去结点的索引 其中若删去第一个结点&#xff0c;返回head.next; /*** Definition for singly-linked list.* public …

python:编写一个函数查找字符串中的最长公共前缀

最近在csdn网站上刷到一个题目&#xff0c;题目要求编写一个函数查找字符串中的最长公共前缀&#xff0c;题目如下&#xff1a; 给出的答案如下&#xff1a; from typing import List def longestCommonPrefix(strs:List[str]) -> str:if len(strs) 0:return i 0 #代…

828华为云征文——使用Flexus云服务器X实例CentOS镜像下创建MySQL服务器教程

一、概述 1.1 前言 当前正值华为云盛大的828 B2B企业庆典&#xff0c;其中Flexus X实例的特惠活动尤为吸引人眼球。对于追求极致算力表现&#xff0c;并期望在自建MySQL数据库、Redis缓存系统及Nginx服务器部署上获得卓越性能的企业用户而言&#xff0c;这无疑是一个不可多得的…

SVTR文字识别

论文地址&#xff1a;https://arxiv.org/abs/2205.00159 notes&#xff1a; 论文2.5中说的N nodes&#xff0c;就是输出的类别数量&#xff0c;英文37&#xff0c;中文6625&#xff0c;英文37说的是最简单的英文文字识别任务&#xff0c;不区分大小写&#xff0c;就是26个字母…

数据湖 Data Lake-概述

Data Lake 1. 数据湖的定义 数据湖是一种存储系统&#xff0c;用于集中存储大量的原始数据&#xff0c;可以按数据本来的原始格式进行存储&#xff0c;用户可以在需要时提取和分析这些数据。 A data lake is a centralized repository designed to hold vast volumes of data …