【学习笔记】手写 Tomcat 七
目录
一、优化 Dao
1. 设置 UserDaoImpl 为单例模式
2. 创建 Dao 工厂
3. 在 Service 层获取 UserDao 的实例
二、优化 Service
1. 设置 UserServiceImpl 为单例模式
2. 创建 Service 工厂
3. 在 Servlet 层获取 Service 实现类的对象
三、优化 Servlet
1. 使用配置文件
1. 创建配置文件
2. 创建工具类
3. 调用工具类获取对象
测试
2. 使用注解的方式
1. 创建自定义注解
2. 使用注解
3. 创建工具类
4. 获取对象
测试
四、作业
1. 绘制架构图
2. 了解 NIO
一、优化 Dao
1. 现在在 Service 层,调用Dao层的实现类都要 new 一下,这样就会创建很多对象,比较占内存,比如 Service 层的其他实现类也会调用 UserDao,那么只需要创建一次 UserDao 的对象就行了
使用单例模式
2. 既然 Service 层的很多方法可能也会调用UserDao,如果以后需要更换 UserDao的实现类,那么所有调用 UserDao 的地方都需要修改一下,这样比较麻烦
使用工厂模式获取 UserDao 的对象,如果要修改,只需要在工厂类修改即可
1. 设置 UserDaoImpl 为单例模式

2. 创建 Dao 工厂
用于获取 Dao 的不同实例

3. 在 Service 层获取 UserDao 的实例

二、优化 Service
和 Dao 层同理,在 Servlet 层也多次创建了 UserService 的实现类,比如登录和修改密码都用到了UserService
1. 设置 UserServiceImpl 为单例模式

2. 创建 Service 工厂
package com.shao.Service;import com.shao.Service.Impl.UserServiceImpl;public class ServiceFactory {public static UserService getUserService() {return UserServiceImpl.getInstance();}
}
3. 在 Servlet 层获取 Service 实现类的对象

三、优化 Servlet
现在的情况是当 Tomcat 接收一个请求后,会先判断是请求的静态资源还是动态资源,如果是动态资源,还要继续判断请求的是哪个功能,然后调用相应的 Servlet 执行
这样有些缺点,当功能很多时,一个个判断请求的是哪个功能,效率不高,而且代码不够优雅,并且来一个请求就会创建一下 Servlet 对象,比较消耗资源,那如何解决呢?
解决方案是:我们先把所有的 Servlet 对象创建好,然后放到一个容器(集合)里,当有请求时,取出对应的 Servlet 对象执行。这是不是很熟悉的感觉?池化思想
那么问题来了,系统怎么知道哪些 Servlet 需要创建对象?
1. 使用配置文件
把要创建的 Servlet 的全类名放到配置文件,然后读取配置文件,通过反射技术创建对象
1. 创建配置文件

2. 创建工具类

package com.shao.Utils;import com.shao.Servlet.BaseServlet;import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;public class ServletUtil {// 获取当前系统分隔符final static String FENGEFU = File.separator;// 存放 Servlet 对象static HashMap<String, BaseServlet> map = new HashMap<>();// 读取配置文件的内容static Properties properties = new Properties();static {try {properties.load(new FileInputStream("config" + FENGEFU + "Servlet.properties"));// 把 Key 放到集合中Set<Object> keySet = properties.keySet();for (Object key : keySet) {String value = properties.getProperty(key.toString());// 通过全类名获取 Class 对象,Class 对象记录了这个类的全部信息Class<?> aClass = Class.forName(value);/** 底层原理:* 1. 调用构造器,通过 Class 对象找到对应类型的无参构造器* 2. 实例化对象:使用构造器创建一个新的对象实例* 3. 返回实例* */BaseServlet baseServlet = (BaseServlet) aClass.newInstance();// 添加到 map 集合map.put(key.toString(), baseServlet);}} catch (Exception e) {e.printStackTrace();}}// 对外提供一个接口,获取集合中 key 对应的 value ,value 是 Servlet 对象public static BaseServlet getServletClass(String key) {return map.get(key);}}
3. 调用工具类获取对象
在响应类调用 ServletUtil 获取Servlet 实例

package com.shao.net;import com.alibaba.fastjson2.JSON;
import com.shao.Servlet.BaseServlet;
import com.shao.Servlet.ChangePasswordServlet;
import com.shao.Servlet.LoginServlet;
import com.shao.Utils.DBConnectUtil;
import com.shao.Utils.ServletUtil;
import com.shao.Utils.responseDTO;import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.*;public class HttpResponse {/*** 输出流*/private OutputStream os;/*** 解析请求信息的对象*/private HttpRequest httpRequest;public HttpResponse(OutputStream os, HttpRequest httpRequest) {this.os = os;this.httpRequest = httpRequest;}public void response(String filePath) {//判断请求的是否为静态文件if (StaticResourceHandler.isLikelyStaticResource(httpRequest.getRequestModule())) {// 获取静态资源一般是 GET 请求方法if (httpRequest.getRequestMethod().equals("GET")) {// 响应静态资源responseStaticResource(filePath);}} else {// 处理动态请求System.out.println("请求动态资源");// 获取 Servlet 对象,参数是请求的模块名BaseServlet servlet = ServletUtil.getServletClass(httpRequest.getRequestModule());// 如果没有找到对应的 Servlet ,返回 404if (servlet == null) {responseStaticResource("webs/pages/not_Found404.html");return;}// 调用 service 方法servlet.service(httpRequest, this);// if (httpRequest.getRequestModule().equals("/doLogin")) {
//
// // 创建 登录的 Servlet 对象
// LoginServlet loginServlet = new LoginServlet();
//
// // 调用 service 方法,响应数据也封装在 servlet 里
// loginServlet.service(httpRequest, this);
//
//
// } else if ("/ChangePassword".equals(httpRequest.getRequestModule())) {
//
// ChangePasswordServlet servlet = new ChangePasswordServlet();
//
// servlet.service(httpRequest, this);
// } else if ("/test".equals(httpRequest.getRequestModule())) {
// send(JSON.toJSONBytes(new responseDTO(200, "ok", "test")));
// }}}/*** 响应静态资源*/private void responseStaticResource(String filePath) {// 读取文件byte[] fileContents = StaticResourceHandler.getFileContents(filePath);// 判断文件是否存在,不存在则返回 404 的页面if (fileContents == null) {try {FileInputStream fis = new FileInputStream("webs/pages/not_Found404.html");fileContents = new byte[fis.available()];fis.read(fileContents);fis.close();} catch (Exception e) {e.printStackTrace();}}// 响应协议String protocol = httpRequest.getRequestProtocol();// 文件媒体类型String fileMimeType = StaticResourceHandler.getFileMimeType(filePath);try {os.write((protocol + " 200 OK\r\n").getBytes());os.write(("Content-Type: " + fileMimeType + "\r\n").getBytes());os.write(("Content-Length: " + fileContents.length + "\r\n").getBytes());os.write("\r\n".getBytes());os.write(fileContents);os.flush();System.out.println("响应成功");os.close();} catch (IOException e) {e.printStackTrace();}}public void send(byte[] content) {// 获取请求协议String protocol = httpRequest.getRequestProtocol();try {os.write((protocol + " 200 OK\r\n").getBytes());os.write(("Content-Type: " + "text/html;charset=utf-8" + "\r\n").getBytes());os.write(("Content-Length: " + content.length + "\r\n").getBytes());os.write("\r\n".getBytes());os.write(content);os.flush();System.out.println("响应成功");os.close();} catch (IOException e) {e.printStackTrace();}}
}
测试

2. 使用注解的方式
在需要创建的 Servlet 的类名上加上自定义注解,然后通过反射技术创建对象
1. 创建自定义注解

package com.shao.Annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyServlet {String value();
}
2. 使用注解

3. 创建工具类

package com.shao.Utils;import com.shao.Annotation.MyServlet;
import com.shao.Servlet.BaseServlet;import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;public class ServletByAnnoUtil {// 存放 Servlet 对象private static HashMap<String, BaseServlet> map = new HashMap<>();// 要扫描包的路径private static String PackagePath = "com.shao.Servlet";static {// 获取类加载器ClassLoader classLoader = ServletByAnnoUtil.class.getClassLoader();// 获取包的完整路径URL resource = classLoader.getResource(PackagePath.replace(".", "/"));if (resource == null) {throw new RuntimeException("Package path not found:" + PackagePath);}try {// 创建目录对象File packageDir = new File(resource.toURI());// 获取目录下所有文件File[] files = packageDir.listFiles();/** 1. 判断文件是否为 .class 文件* 2. 获取文件名,去掉 .class 后缀* 3. 包路径 + 文件名,拼接成全类名* 4. 获取 Class 对象* 5. 判断 Class 对象是否有 MyServlet 注解* 6. 如果有,判断是否 BaseServlet 类,或者继承 BaseServlet* 7. 创建对象,获取注解的值,存放到 map 中** */for (File file : files) {if (file.getName().endsWith(".class")) {// 获取全类名String className = PackagePath + "." + file.getName().substring(0, file.getName().lastIndexOf("."));// 获取 Class 对象Class<?> aClass = Class.forName(className);// 判断是否有 MyServlet 注解if (aClass.isAnnotationPresent(MyServlet.class)) {// 判断是否继承 BaseServletif (BaseServlet.class.isAssignableFrom(aClass)) {// 创建对象BaseServlet servlet = (BaseServlet) aClass.newInstance();// 获取注解的值MyServlet annotation = aClass.getAnnotation(MyServlet.class);map.put(annotation.value(), servlet);}}}}} catch (URISyntaxException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}// 对外提供一个接口,获取 Servlet 对象public static BaseServlet getServletClass(String key) {return map.get(key);}
}
4. 获取对象

测试

四、作业
1. 绘制架构图
2. 了解 NIO
相关文章:
【学习笔记】手写 Tomcat 七
目录 一、优化 Dao 1. 设置 UserDaoImpl 为单例模式 2. 创建 Dao 工厂 3. 在 Service 层获取 UserDao 的实例 二、优化 Service 1. 设置 UserServiceImpl 为单例模式 2. 创建 Service 工厂 3. 在 Servlet 层获取 Service 实现类的对象 三、优化 Servlet 1. 使用配置…...
QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法
1. 引言 在现代应用程序开发中,多线程编程是一个关键技术,能够显著提高程序的效率和响应速度。Qt 是一个跨平台的 C 框架,其中 QThread 类是实现多线程编程的核心类。本文将深入详解 QThread 的基本概念、使用方法及其在实际应用中的重要性。…...
【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)
后台管理快速开发框架使用笔记(微服务版之本地开发环境篇) 后台管理快速开发框架使用笔记(微服务版之本地开发环境篇) 后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)前言一、如何获取项目&#…...
设计模式、系统设计 record part01
技术路线: 工程师》设计师》分析师》架构师 管理路线: 项目经理》技术经理 工程师: 编程技术、测试技术 设计师: 工程师设计技术 分析师: 设计师分析技术 架构师: 分析师架构技术 项目经理: 时间…...
服务器与普通电脑的区别是什么?
服务器作为企业进行线上业务所使用的网络设备,大多数的用户对于服务器都有一定的了解,而普通的电脑则是人们在进行日常娱乐活动中经常会用到的设备,本文就来探讨一下服务器与普通电脑之间的区别是什么吧! 普通的电脑就是我们通常所…...
Vue3学习(六)Vue3 + ts几种写法
前言 官网提到组合式api和选项式api 选项式api其实就是vue2的写法,组合式api是vue3的新写法(组合式api可以在script中使用setup()也可以使用<script setup>,<script setup>是setup(ÿ…...
【前端】ES6:Proxy代理和Reflect对象
文章目录 1 Proxy代理1.1 get方法1.2 set方法1.3 has方法1.4 this问题 2 Reflect对象2.1 代替Object的某些方法2.2 修改某些Object方法返回结果2.3 命令式变为函数行为2.4 配合Proxy 1 Proxy代理 Proxy如其名,它的作用是在对象和和对象的属性值之间设置一个代理&am…...
基于微信开发助手企鹅音乐微信小程序的设计与实现(源码+文档+讲解)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...
学习Spring Boot,应该从哪里开始学起
文章目录 前言1. Java基础2. Spring框架基础3. Spring Boot入门4. 搭建Spring Boot项目5. 编写RESTful API6. 数据库操作7. 安全性和测试8. 部署和运维9. 实践和项目总结前言 学习Spring Boot,应该从哪里开始学起 学习Spring Boot,你可以从以下几个步骤开始学起: 1. Java基…...
【JavaEE初阶】深入解析死锁的产生和避免以及内存不可见问题
前言: 🌈上期博客:【后端开发】JavaEE初阶—线程安全问题与加锁原理(超详解)-CSDN博客 🔥感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 ⭐️小编会在后端开发的学习中不断更新~~~ &#…...
企微群管理软件:构建高效社群运营的新引擎
在数字化营销日益盛行的今天,企业微信(简称“企微”)群作为企业与用户直接互动的重要平台,其管理与运营效率直接关系到企业的品牌形象、用户满意度及市场影响力。企微群管理软件,作为专为企微社群设计的高效管理工具&a…...
CORE 中间件、wwwroot
ASP.NET Core中间件组件是被组装到应用程序管道中以处理HTTP请求和响应的软件组件(从技术上来说,组件只是C#类)。 ASP.NET Core应用程序中的每个中间件组件都执行以下任务。 选择是否将 HTTP 请求传递给管道中的下一个组件。这可…...
SpringBoot 与 Maven 快速上手指南
SpringBoot 与 Maven 快速上手指南 在Java开发领域,Spring Boot和Maven是两个极其重要的工具,它们极大地简化了企业级应用的开发和构建过程。Spring Boot通过自动配置和起步依赖等特性,让开发者能够快速搭建起一个Spring应用;而M…...
大觅网之自动化部署(Automated Deployment of Da Mi Network)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…...
【C++】入门基础知识-1
🍬个人主页:Yanni.— 🌈数据结构:Data Structure. 🎂C语言笔记:C Language Notes 🏀OJ题分享: Topic Sharing 目录 前言: C关键字 命名空间 命名空间介…...
Redis一些简单通用命令认识常用数据类型和编码方式认识Redis单线程模型
通用命令 get() / set() 这是Redis中两个最为核心的命令。 set插入 这里的key 和 value都是字符串,我们可以加双引号 或者单引号,或者不加。 get查找 如果查询的key值不存在,那么会返回一个 nil ,也就是代表空 在Redis中命令…...
使用电子模拟器 Wokwi 运行 ESP32 示例(Arduino IDE、VSCode、ESP32C3)
文章目录 Wokwi 简介安装客户端(Mac/Linux)创建 Token Arduino IDEVSCode 配置安装 wokwi 插件打开编译后目录 ESP32C3 示例Arduino IDE创建模拟器运行模拟器 Wokwi 简介 Wokwi 是一款在线电子模拟器。您可以使用它来模拟 Arduino、ESP32、STM32 以及许…...
C嘎嘎入门篇:类和对象(1)
前言: 小编在之前讲述了C的部分入门基础,读者朋友一定要掌握好那些,因为C的学习和C有点不同,C的知识都是比较连贯的,所以我们学好了前面才可以学习后面的内容,本篇文章小编将会讲述C真正的入门篇࿱…...
tomcat服务搭建部署ujcms网站
tomcat服务搭建部署ujcms网站 关闭selinux和防火墙 setenforce 0 && systemctl stop firewalld安装java环境 #卸载原有java8环境 yum remove java*#上传java软件包,并解压缩 tar -xf openjdk-11.0.1_linux-x64_bin.tar.gz && mv jdk-11.0.1 jdk11…...
unity_Occlusion_Culling遮挡剔除学习
unity_Occlusion_Culling遮挡剔除学习 文档: https://docs.unity.cn/cn/2019.4/Manual/occlusion-culling-getting-started.html没彻底搞明白,但是会用,虽然也不熟练 设置遮挡剔除 打开遮挡剔除面板 设置场景物体。设置为静态 设置场景 烘…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
