【学习笔记】手写 Tomcat 七

手写一个Tomcat (参考了公众号:java架构沉思录中的文章:教你写一个迷你版的toncat 原文:https://www.jianshu.com/p/dcelee01fb90) 作为一个java学习的起步者,对tomcat的认识还是有很多的欠缺,在无意中发现了这篇文章,便在自己的环境下尝试搭建,收获良多: 分以下几个步骤: (1)提供Socket服务 (2)进行请求的转发 (3)把请求和响应封装成request/... 阅读详情

目录

一、优化 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 ,返回 404
            if (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)) {
                        // 判断是否继承 BaseServlet
                        if (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 上述流程为简易的 Tomcat 实现提供了一个基本框架,通过监听和接收连接、解析请求、构建响应和发送响应的流程,可以实现一个最基本的 HTTP 服务器。以上是简易的 Tomcat 结构概述,实际上,Tomcat 在设计上更加复杂和灵活,包括更多的组件和功能,比如 Realm(安全认证和授权)、Valve(请求处理的管道过滤器)、JSP 引擎、会话管理等。Request Processor(请求处理器):接收到连接器转发的请求后,负责解析请求内容、将请求转发给适当的组件进行处理,并将处理结果返回给连接器。 阅读详情

相关推荐

AI辅助发明专利撰写实战:30分钟高效生成高质量技术专利文档

在技术专利撰写领域,尤其是软件、算法或系统架构相关的发明专利,如何将创新的技术构思快速、规范地转化为符合《专利法》要求的申请文件,是许多开发者和技术团队面临的共同挑战。传统的人工撰写方式不仅耗时耗力,对撰写人的法律和技术双重背景要求极高,还容易因格式或术语不规范导致审查意见增多。本文将分享一套利用 AI 辅助工具,系统化、高效率地完成一篇高质量发明专利说明书撰写的实战流程。通过清晰的步骤拆解、可复制的提示词(Prompt)工程以及关键注意事项,即使是首次接触专利撰写的开发者,也能在短时间内掌握从技术方案梳理

子夜轻风 952

手写Tomcat

tomcat提供的服务: 第一:提供Socket服务 第二:进行请求的分发 第三:需要把请求和响应封装成request/response

书生的博客 4098

从MC到DC:三维重建算法的核心原理与实战对比

本文深入对比了三维重建领域两大经典算法Marching Cubes(MC)与Dual Contouring(DC)的核心原理与实战表现。MC算法原理直观、实现简单,但存在特征丢失和二义性问题;DC算法通过求解二次误差函数(QEF)和利用法向量信息,能更好地保留模型的尖锐特征,但计算更复杂。文章结合医学影像处理实例,为开发者根据速度、精度和特征保真度需求选择合适算法提供了清晰指南。

weixin_29251131的博客 1032

自己手写tomcat项目

在Servlet(接口中)有:1.init():初始化servlet2.getServletConfig():获取当前servlet的配置信息3.service():服务器(在HttpServlet中实现,目的是为了更好的匹配http的请求方式)4.getServletInfo():获取servlet当前运行过程中的信息5.destroy():销毁,回收内存在I/O中应包含:1.请求头(f12+网络) 2.请求方式(get/post) 3.请求内容。

2302_79585857的博客 1799

Windows 10/11 零基础配置 SillyTavern 连接 Claude API 完整指南(含 Node.js 环境搭建)

本文提供Windows 10/11系统下零基础配置SillyTavern连接Claude API的完整教程,涵盖Node.js环境搭建、SillyTavern初始化及API连接配置等关键步骤。通过详细的操作指南和实用技巧,帮助用户快速搭建AI聊天界面并优化对话体验,实现与Claude AI的高效交互。

weixin_30642561的博客 134

Arcgis填补栅格空缺值Nodata

Arcgis填补栅格空缺值Nodata

pass17的博客 1万+

【学习笔记】手写 Tomcat -- 预备知识

I 是 Input,O 是Output,也就是输入输出流。当我们操作数据时,以内存为基准,把数据读取到内存中是输入,把数据从内存写入到硬盘是输出。

LearnTech_123的博客 1644

SIMetrix教程-001.SIMetrix软件简介与安装

  由于某些原因需要用到SIMetrix仿真软件,然而网上的资料并不是特别多,故在此记录一下这款仿真软件的学习过程,也给有需要的人提供一些参考,免走弯路。如果使用过Pspice或者LTspice软件,学习SIMetrix会很容易入门,仿真软件都是大同小异的,会一样后都可以快速入门其他软件。   SIMetrix是一款以SPICE模型为基础的仿真软件,集成了SIMetrix和SIMPLIS两款内核,为工程师提供了可靠的、易于使用的、并完美结合了速度与精度的电路仿真方式。官方的话不多说,和LTspice一样,优

gaoyong_wang的博客 2万+

【学习笔记】手写 Tomcat 六

昨天使用了数据库连接池,我们了解了连接池的优点,那么也可以使用线程池来管理线程,java自带的线程池的参数有 核心线程数,最大线程数,线程活跃时间,时间单位,任务队列,线程工厂,拒绝策略。为了学习了解线程池,我们先手写一个简单的线程池,只需要做到核心线程可重复利用就行。

LearnTech_123的博客 1763

STM32串口通信实战:从CubeMX配置到printf重定向全解析

串口通信是嵌入式系统中最基础且广泛使用的数据交换方式,它基于异步串行通信原理,通过TX(发送)和RX(接收)两根信号线实现设备间的全双工通信。其技术价值在于协议简单、硬件成本低、可靠性高,是单片机与上位机调试、传感器数据采集、设备控制等场景的首选方案。在STM32等ARM Cortex-M系列MCU中,USART外设通过硬件自动处理时序,大大减轻了CPU负担。本文聚焦于嵌入式竞赛与开发中的高频需求,以STM32G431RBT6为例,详解通过CubeMX配置USART1、实现按键触发数据发送、并完成print

weixin_30603633的博客 545

群晖如何安装软路由OpenWrt/iStoreOS作为旁路由(含网络设置、设置密码、还原出厂设置等linux命令ipk包)

openwrt的固件。

XiaoqiangClub的博客 7017

手写简易Tomcat

通过手写tomcat对java中的网络通信有了更进一步的了解

Java新生代 1411

ENS160传感器原理图设计,已量产(空气质量传感器)

ENS160 是一款集成式数字空气质量传感器,核心优势源于 “MEMS 传感元件 + 内置微处理器” 的架构。

不脱发的程序猿 569

18_1手写Tomcat学习笔记

tomcat学习笔记 文章目录1 tomcat简介1.1 web服务器1.2 tomcat服务器1.2.1 下载1.2.2 安装1.2.3 配置1.2.4 tomcat目录结构1.2.5 如何去访问一个web应用的某个文件1.2.6 在web服务器中安装web应用1.2.7 配置虚拟目录1.2.8 虚拟主机1.3 浏览器访问web站点原理2 手写tomcat_静态资源3 手写tomcat_动态资源3.1 tomcat工作原理3.2 http get请求格式3.2 http post请...

tangguanlin2008的博客 3741

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【学习笔记】手写 Tomcat 二

在昨天的基础上,再进一步优化,能够响应HTML文件,图片等静态资源。

LearnTech_123的博客 1485

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

从零开始手写Tomcat,一文彻底搞懂Tomcat运行流程(附源码)

一:理论铺垫 tomcat的功能是什么? tomcat是java的一个中间件,浏览器发出HTTP请求后经过tomcat中间件,转发到目的服务器,目的服务器返回响应消息,通过tomcat返回给浏览器。tomcat的使用很简单,但是作为合格的程序员,光会用可不行,接下来就通过手写一个tomcat彻底搞懂tomcat。 在手写Tomcat之前,首先重温一下http和servlet,http协议...

Java鱼仔的博客 7764

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

手写Tomcat框架

模型核心逻辑并发能力复杂度BIO一请求一线程,全程阻塞低简单NIO线程 + Selector 管多连接中高中等AIO异步通知,线程不等待高复杂2.扫描包的类用的是一个工具类进行实现的2.1首先 进行遍历获取类信息2.2通过反射机制,从目标类(clazz)中提取它上面标注的@WebServlet注解对象2.3这里获取类的无参构造方法 ---创建新的对象并强转向上转型为。

2302_80192044的博客 978

【学习笔记】手写 Tomcat 五

我们把功能都写在了 Servlet,这样有几个缺点,一是不够低耦合 高内聚,二是不方便协同开发等。我们可以使用三层架构来解决这个问题。三层架构区分层次的目的是为了 “高内聚,低耦合”。

LearnTech_123的博客 1184

如何基于Netty手写简单的Tomcat?

Spring MVC 处理业务逻辑的,我们感知不到它的存在,平常我们只需要写Controller ,这是因为DispatcherServlet拦截了所有的请求。SpringBoot 提倡零配置,使用注解方式是Spring MVC 的主流。Spring 容器启动时会扫描相关类,并且j解析类上、方法上注解。类上URL作为基础路径和方法上URL拼接作为最终URL ,而对应方法即最终URL的处理器。相关的映射关系也被称为handler mappings。

happycao123的博客 1063

手写一个Tomcat服务器

作为java开发者,都知道Tomcat是一个家户喻晓的Web服务器。手写一个Tomcat对我们理解Tomcat的工作原理,还有servlet的执行流程有很大的帮助,下面是我手写一个简要版Tomcat的总结和收获! 首先,先还原Tomcat的本质: 相信大家之前在开发的时候,虽然已经能上手开发web程序,但是对于Servlet的执行流程以及项目还会存在很多疑问,下面是我总结的我之前的疑问也给出相应...

yechengchao的博客 2051

如何用JAVA手写一个Tomcat

Socket(套接字)是计算机网络中用于进程间通信的核心技术,它允许不同主机或同一主机上的不同程序通过网络交换数据Socket 是什么?定义:Socket 是操作系统提供的抽象接口,本质上是通信的端点(Endpoint)。它封装了底层网络协议(如TCP/IP或UDP)的复杂性,提供了一组简单的API【API(Application Programming Interface,应用程序编程接口)是软件系统之间交互的桥梁,定义了如何请求服务、传递数据以及返回结果的规则。】供应用程序使用。关键要素IP地址。

2301_80391652的博客 2580

Tomcat原理之手写

目录 一、准备工作 1.1 Tomcat概要 1.2 技术储备 二、实现步骤 三、相关类的实现和介绍 3.1 包结构 3.2 Servlet配置类 3.3 Tomcat实现类 3.4 请求响应实现类Request/Response 3.5 工具类HttpUtil/FileUtil 3.6 Servlet抽象和测试实现类 四、测试运行 4.1 成功请求servlet(200...

天天向上 1048
上一篇: 【学习笔记】手写 Tomcat 六
下一篇: 【学习笔记】手写 Tomcat 八
LearnTech_123
博客等级 码龄3年 445粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值