【学习笔记】手写 Tomcat 五

news2024/11/15 8:45:47

目录

一、优化 Servlet

创建一个抽象类

继承抽象类

二、三层架构 

业务逻辑层

数据访问层

1. 在 Dao 层操作数据库

2. 调用 Dao 层,实现业务逻辑功能

3. 调用 Service 层,响应数据

测试

三、数据库连接池

1. 手写数据库连接池

2. 创建数据库连接池的类

3. 初始化连接池

4. 在 Dao 层获取数据库连接

5. 归还数据库连接

作业

1. 3w1h的方式,了解单例模式

2. 了解其他连接池


一、优化 Servlet

昨天,我们把响应动态资源的功能放到了 Servlet,一个 Servlet 就是一个功能

当 Servlet 多了之后,我们会发现,每个 Servlet 里都有 service 方法,那有什么方法可以简化呢?

这里就可以使用 Java 的三大特性之一 继承

创建一个抽象类

定义普通的 service 方法,和 doGet , doPost 抽象方法

继承抽象类

在每个 Servlet 中继承这个抽象类,然后就可以删除 service 方法了

二、三层架构 

我们把功能都写在了 Servlet,这样有几个缺点,一是不够低耦合 高内聚,二是不方便协同开发等

我们可以使用三层架构来解决这个问题

三层架构是一种设计思想,分为表示层业务逻辑层数据访问层

表示层 Servlet:负责处理用户的请求和响应用户需要的数据

业务逻辑层 Service:负责业务上逻辑的处理,也是表示层和数据层之间的桥梁,

数据访问层 Dao:操作数据库,进行增删改查,并将结果传给业务逻辑层

三层架构区分层次的目的是为了 “高内聚,低耦合”。开发人员分工更明确

业务逻辑层

创建 UserService 接口,用于专门处理用户相关的业务逻辑,比如用户登录,注册,修改密码等

创建接口的实现类,在实现类里实现业务相关逻辑

为什么要创建接口,再创建实现类去实现接口呢?直接创建类,在类里实现业务逻辑不行吗?

当然可以,不过面向接口有很多优点。比如解耦,当实现类需要改变,不会影响 Servlet 层。还有规范化等优点

数据访问层

创建 UserDao 接口,并创建实现类,和业务逻辑层同理

在接口定义抽象方法

1. 在 Dao 层操作数据库

在实现类实现接口,并把 Servlet 层的关于数据库操作的代码放到实现类里

package com.shao.Dao.Impl;

import com.shao.Dao.UserDao;
import com.shao.Utils.DBConnectUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class UserDaoImpl implements UserDao {

    @Override
    public int Login(String account, String password) {

        Connection connection = null;
        PreparedStatement pstmt = null;
        ResultSet resultSet = null;
        int result = 0;

        try {

            // 3. 获取数据库连接
            connection = DBConnectUtil.getConnection();

            // 4. 获取可执行对象
            String SQL = "select count(*) from train.users where account = ? and password = ?";
            pstmt = connection.prepareStatement(SQL);

            // 设置占位符的值
            pstmt.setString(1, account);
            pstmt.setString(2, password);

            // 5. 执行sql语句,获取结果集
            resultSet = pstmt.executeQuery();

            // 6. 结果处理
            if (resultSet.next() && resultSet.getInt(1) > 0) {

                // 表示可以查询到数据
                result = 1;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            // 7. 释放资源
            DBConnectUtil.releaseSource(connection, pstmt, resultSet);
        }

        // 返回结果给业务逻辑层
        return result;
    }
}

2. 调用 Dao 层,实现业务逻辑功能

在 Service 层调用 Dao 层,并实现业务逻辑功能

package com.shao.Service.Impl;

import com.shao.Dao.Impl.UserDaoImpl;
import com.shao.Dao.UserDao;
import com.shao.Service.UserService;
import com.shao.Utils.responseDTO;

public class UserServiceImpl implements UserService {

    // 创建一个UserDao的实现类对象
    private UserDao userDao = new UserDaoImpl();
    
    @Override
    public responseDTO Login(String account, String password) {

        // 调用UserDao的Login方法
        int login = userDao.Login(account, password);
        
        // 判断是否登录成功
        if (login == 1) {
            return new responseDTO(200, null, "登录成功");
        } else {
            return new responseDTO(201, null, "账号或密码错误");
        }
    }
}

3. 调用 Service 层,响应数据

在 Servlet 层调用 Service 层,然后把业务逻辑层返回的数据响应到客户端

package com.shao.Servlet;

import com.alibaba.fastjson2.JSON;
import com.shao.Service.Impl.UserServiceImpl;
import com.shao.Service.UserService;
import com.shao.Utils.responseDTO;
import com.shao.net.HttpRequest;
import com.shao.net.HttpResponse;


public class LoginServlet extends BaseServlet {
    responseDTO responseDTO = null;

    public void doGet(HttpRequest request, HttpResponse response) {

        // 获取请求参数
        String account = request.getRequestBodyParams().get("account");
        String pwd = request.getRequestBodyParams().get("password");

        // 调用 Service 层的Login方法
        UserService userService = new UserServiceImpl();
        responseDTO = userService.Login(account, pwd);

        // 响应数据
        response.send(JSON.toJSONBytes(responseDTO));
    }

    public void doPost(HttpRequest request, HttpResponse response) {
        responseDTO = new responseDTO(400, null, "不支持POST提交方法");
        response.send(JSON.toJSONBytes(responseDTO));
    }
}

测试

三、数据库连接池

在测试的时候,我们可能会发现,第一次登录的时候,数据响应会比较慢,第二次之后就比较快了

这是因为第一次登录需要与数据库进行连接,第二次访问时,数据库已经准备好了,所以比较快

而且,当用户很多时,发起一次请求就需要建立一个数据库连接,不用了就断开连接,这样比较消耗资源,也比较耗时

那如何解决这个问题呢?

解决方案是使用数据库连接池,在系统空闲的时候,创建好数据库连接,当用户发起请求后就从连接池取出一条连接使用,当用完了再把连接放到连接池中,这样就节约了资源,也提升了效率

数据库连接池可以使用市面上的,比如Druid,Apache DBCP 等

1. 手写数据库连接池

这里我们手写一个简单的数据库连接池

在设计数据库连接池之前,我们需要确定,这个连接池一个就可以了,所以连接池的类就只能有一个实例(对象),这就可以用到单例设计模式。使用哪种方式创建单例模式,如果使用饿汉式,需要提前加载好连接池

2. 创建数据库连接池的类

package com.shao.Utils;

import java.sql.Connection;
import java.util.LinkedList;

public class DBConnectPool {

    // 定义一个双向链表,存放连接
    private static final LinkedList<Connection> connectionPool = new LinkedList<>();

    // 连接池最大连接数
    private static final int maxSize = 10;


    // 定义一个成员变量,存储单例对象
    private static DBConnectPool dbConPool;

    // 静态代码块,在类加载时,就会执行代码块,且只执行一次
    // 调用工具类获取连接,然后添加到连接池中
    static {
        for (int i = 0; i < maxSize; i++) {
            Connection connection = DBConnectUtil.getConnection();
            if (connection != null) {
                connectionPool.add(connection);
            }
        }
    }

    // 私有构造方法,防止外部实例化
    private DBConnectPool() {
    }

    // 对外提供一个接口,用于获取DBConnectPool对象
    public static DBConnectPool getInstance() {
        synchronized (DBConnectPool.class) {
            if (dbConPool == null) {
                dbConPool = new DBConnectPool();
            }
            return dbConPool;
        }
    }

    // 获取连接
    public Connection getConnection() {
        // 从连接池删除一个连接,然后把连接返回
        // 以同步的方法获取连接,保证线程安全
        synchronized (DBConnectPool.class) {
            Connection poll = connectionPool.poll();
            return poll;
        }
    }

    // 释放连接
    public void releaseConnection(Connection connection) {
        if (connection == null) {
            return;
        }
        //把连接放回连接池
        synchronized (DBConnectPool.class) {
            connectionPool.add(connection);
        }
    }
}

3. 初始化连接池

用饿汉式创建连接池,所以需要在启动 Tomcat 的时候就加载好连接池

4. 在 Dao 层获取数据库连接

5. 归还数据库连接

在数据库连接工具类的释放资源的方法中可以删掉释放数据库连接的参数,改为使用连接池的归还连接的方法

作业

1. 3w1h的方式,了解单例模式

2. 了解其他连接池

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

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

相关文章

C语言题目之单身狗2

文章目录 一、题目二、思路三、代码实现 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、题目 二、思路 第一步 在c语言题目之打印单身狗我们已经讲解了在一组数据中出现一个单身狗的情况&#xff0c;而本道题是出现两个单身狗的情况。根据一个数…

当Navicat报错 Can not connect to MySQL server的解决方法!

今天运行数据库时突然弹出一个error&#xff1a; 原因&#xff1a;MySQL的服务没有打开&#xff0c;需要检查MySQL的开启状态即可。 具体做法&#xff1a; 1.右键“开始”&#xff0c;点击“计算机管理” 2. 选择“服务和应用程序”&#xff0c;并点击“服务” 3.在服务中找…

ESP32-WROOM-32 [创建AP站点-TCP服务端-数据收发]

简介 ESP32 创建TCP Server AP站点&#xff0c; PC作为客户端连接站点并收发数据 指令介绍 注意,下面指令需要在最后加上CRLF, 也就是\r\n(回车换行) ATRESTORE // 恢复出厂设置 ATCWMODE2 // 设置 Wi-Fi 模式为 softAP ATCIPMODE0 // 需要数据传输模式改为0&#xff0c; 普通…

Cesium 绘制可编辑点

Cesium Point点 实现可编辑的pointEntity 实体 文章目录 Cesium Point点前言一、使用步骤二、使用方法二、具体实现1. 开始绘制2.绘制事件监听 三、 完整代码 前言 支持 鼠标按下 拖动修改点&#xff0c;释放修改完成。 一、使用步骤 1、点击 按钮 开始 绘制&#xff0c;单…

河钢数字PMO牛红卫受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 河钢数字技术股份有限公司项目管理部PMO牛红卫受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“从技术到领导力——项目经理成长进阶之道”。大会将于10月26-27日在北京举办&…

知情人称,丹尼尔克雷格在卸任“007”以后他和蕾切尔薇兹的婚姻“产生了奇效”

丹尼尔克雷格、蕾切尔薇兹 虽然丹尼尔克雷格 (Daniel Craig) 因出演詹姆斯邦德 (James Bond) 而成为全球最耀眼的明星之一&#xff0c;实现了自己以及很多人的梦想&#xff0c;但知情人称他与蕾切尔薇兹 (Rachel Weisz) 的婚姻实际上正因此而陷入困境&#xff1b;但现在&#…

C# winforms DataGridView设置数据源自动显示表格

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

高效打造知识图谱,使用LlamaIndex Relik实现实体关联和关系抽取

大家好&#xff0c;文本信息转化为知识图谱的技术&#xff0c;自问世以来一直是研究界的宠儿。大型语言模型&#xff08;LLMs&#xff09;的兴起让这个领域受到更多关注&#xff0c;但LLMs的成本之高令人却步。然而通过对小型模型微调优化&#xff0c;可以找到一种更经济高效的…

没有 Microsoft Wi-Fi Direct Virtual Adapter #2 导致无法打开热点

我的环境 电脑打不开热点 系统 win11 64位 品牌 hp 笔记本电脑 解决方法&#xff1a; https://answers.microsoft.com/zh-hans/windows/forum/all/%E7%A7%BB%E5%8A%A8%E7%83%AD%E7%82%B9%E6%97%A0/9285620a-71d9-4671-b125-4cd607b6371a 解决 &#x1f613; 扫描一下设…

读构建可扩展分布式系统:方法与实践12分布式数据库案例

1. Redis 1.1. 2009年首次发布 1.1.1. 更注重原始性能和简单性&#xff0c;而不是数据安全性和一致性 1.2. 主要吸引力在于它能够同时充当分布式缓存和数据存储 1.3. 维护一个内存中的数据存储&#xff0c;也称为数据结构存储(data structure store) 1.4. 配置Redis将每个…

每日学习一个数据结构-Trie树(字典树)

文章目录 定义节点结构根节点插入操作查找操作删除操作特点应用示例 “Trie”树&#xff0c;又称为前缀树或字典树&#xff0c;是一种专门用于存储字符串的数据结构。它在许多应用程序中都非常有用&#xff0c;特别是在那些需要高效查找、插入和删除字符串的应用场景中。下面是…

2024年华为杯数学建模E题-高速公路应急车道启用建模-基于YOLO8的数据处理代码参考(无偿分享)

利用YOLO模型进行高速公路交通流量分析 识别效果&#xff1a; 免责声明 本文所提供的信息和内容仅供参考。尽管我尽力确保所提供信息的准确性和可靠性&#xff0c;但我们不对其完整性、准确性或及时性作出任何保证。使用本文信息所造成的任何直接或间接损失&#xff0c;本人…

记一次Meilisearch轻量级搜索引擎使用

以前使用的是mysql的全文索引、最开始还行。后续觉得就不好用了&#xff0c;但是服务器资源有限&#xff0c;没法上ES&#xff0c;只好找一个轻量级的搜索引擎、找了半天&#xff0c;决定使用这一个&#xff0c;目前效果还不错的。 参考网址 官网&#xff1a;https://www.meil…

java反射基础知识

1.java的反射机制 Java 反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff1b;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射…

【C语言零基础入门篇 - 16】:栈和队列

文章目录 栈和队列栈栈功能的实现源代码 队列队列功能的实现源代码 栈和队列 栈 什么是栈&#xff1a;功能受限的线性数据结构 栈的特点&#xff1a;先进后出 。例如&#xff1a;仓库进货、出货。 栈只有一个开口&#xff0c;先进去的数据在栈底&#xff08;bottom&#xf…

高版本idea开启热部署找不到compiler.automake.allow.when.app.running

高版本idea开启热部署找不到compiler.automake.allow.when.app.running 1.解决方案 设置----》高级设置 搜索 allow 勾选完成就能使用了

针对论坛系统设计测试用例

针对用户登录设计测试用例 针对站内信编写测试用例

mysql5.7小版本升级

最近因为mysql漏洞问题需要升级版本 目标&#xff1a;5.7.17升级到最新的5.7.44 提前准备&#xff1a;必须 MySQL :: Download MySQL Community Server (Archived Versions) 下载最新的5.7版本5.7.44&#xff0c;下载后&#xff0c;解压备用 ​ 方案1&#xff1a;卸载原有…

网站建设模板选择哪种

在选择网站建设模板时&#xff0c;需要考虑多个因素&#xff0c;包括网站的目的、受众、内容类型以及个性化需求等。以下是一些常见的网站建设模板类型&#xff0c;以及它们的特点&#xff0c;希望对你的选择有所帮助。 企业/商务模板&#xff1a; 企业和商务网站通常需要专业、…

python画图|把X轴标签移动到图像顶端

在前述学习过程中&#xff0c;我们一直使用的是默认的轴坐标&#xff0c;X轴往往置于图像的下端。 有时候&#xff0c;也会有将X轴标签放置在图形顶端的需求&#xff0c;今天就一起学习一下。 【1】官网教程 首先打开官网&#xff0c;可以通过下述链接一步直达&#xff1a; …