【学习笔记】手写 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没彻底搞明白,但是会用,虽然也不熟练 设置遮挡剔除 打开遮挡剔除面板 设置场景物体。设置为静态 设置场景 烘…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
