【手写一个Tomcat】SimpleTomcat-01

2023-11-09

在这里插入图片描述

前言

本文实现一个简易Tomcat,遵循【Tomcat】第八篇:150代码手写Tomcat…

实现

http.TomcatRequest

TomcatRequest.java

package com.sample.http;

import java.io.IOException;
import java.io.InputStream;

public class TomcatRequest {

    /**
     * 请求方法 get post delete put
     */
    private String method;

    private String url;

    public TomcatRequest(InputStream in) {
        try {
            // 1.content用来保存InputStream中的http请求信息
            String content = "";
            byte[] buff = new byte[1024];
            int len = 0;
            if ((len = in.read(buff)) > 0) {
                content = new String(buff, 0, len);
            }

            // 2.对http请求信息进行处理,得到Method与Url
            String line = content.split("\\n")[0];
            String[] arr = line.split("\\s");
            this.method = arr[0];
            this.url = arr[1].split("\\?")[0];

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回url
     * @return String
     */
    public String getUrl() {
        return this.url;
    }

    /**
     * 返回请求方法
     * @return String
     */
    public String getMethod() {
        return this.method;
    }
}

http.TomcatResponse

TomcatResponse.java

package com.sample.http;

import java.io.IOException;
import java.io.OutputStream;

public class TomcatResponse {

    private OutputStream out;

    public TomcatResponse(OutputStream out) {
        this.out = out;
    }

    public void write(String s) throws IOException {
        StringBuilder sb = new StringBuilder();
        // 因为写出的内容要被http协议解析,所以要符合http协议规范,有其要求的响应头(主要是状态码和响应格式)
        sb.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: text/html;\n")
                .append("\r\n")
                .append(s);
        // IO流写出
        this.out.write(sb.toString().getBytes());
    }
}

http.TomcatServlet

TomcatServlet.java

package com.sample.http;

import java.io.IOException;

public abstract class TomcatServlet {

    // 这里的request与response都是Tomcat对象创建好然后传进来的
    public void service(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
        if ("GET".equalsIgnoreCase(tomcatRequest.getMethod())) {
            doGet(tomcatRequest, tomcatResponse);
        }
        else {
            doPost(tomcatRequest, tomcatResponse);
        }
    }

    // 这里是模板方法模式,交给子类去具体实现
    protected abstract void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;

    protected abstract void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException;

}

SimpleTomcat

SimpleTomcat.java

package com.sample;

import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Tomcat核心类
 */
public class SimpleTomcat {

    private int port = 8080;

    private ServerSocket serverSocket;

    private Map<String, TomcatServlet> servletMapping = new HashMap<>();

    private Properties webxml = new Properties();

    private void init() {
        try {
            // 1.加载web.properties文件
            String WEB_INF = this.getClass().getResource("/").getPath();
            FileInputStream fileInputStream = new FileInputStream(WEB_INF + "web.properties");
            webxml.load(fileInputStream);

            // 2.遍历配置文件,寻找url与servlet映射关系配置
            for (Object o : webxml.keySet()) {
                String key = o.toString();
                // 以url结尾的key就是要映射的路径,下面是两条配置示例:
                // servlet.one.url=/firstServlet.do
                // servlet.one.className=com.yzh.tomcat.servlet.FirstServlet
                if (key.endsWith(".url")) {
                    // 去掉.url就是servlet的name(servlet.one)
                    String servletName = key.replaceAll("\\.url$", "");

                    // 2.1 获取到url(/first.do)
                    String url = webxml.getProperty(key);

                    // 2.2 获取对应servlet全类名(com.yzh.tomcat...FirstServlet),并通过反进行实例化
                    String className = webxml.getProperty(servletName + ".className");
                    // 注:这里是将所有Servlet都强转为TomcatServlet,所以一定要继承TomcatServlet
                    TomcatServlet tomcatServlet = (TomcatServlet) Class.forName(className).newInstance();

                    // 3.将url与servlet实例保存到servletMapping中(单例模式)
                    servletMapping.put(url, tomcatServlet);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 启动tomcat
     */
    public void start() {
        // 1.调用init,目的是得到servletMapping的映射关系
        init();

        try {
            // 2.通过BIO创建socket的服务端,在指定端口开始监听
            serverSocket = new ServerSocket(this.port);
            System.out.println("SimpleTomcat已启动,监听的端口是" + this.port);

            // 3.用一个死循环持续等待并处理用户请求
            while (true) {
                Socket client = serverSocket.accept();
                // process是具体处理请求的逻辑,参数是当前连接的Socket
                process(client);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 具体处理请求
     * 1.创建IO流,并包装成Request与Response
     * 2.获取请求Url,取出对应Servlet进行处理
     */
    private void process(Socket client) throws IOException {
        // 1.获取IO流,并封装成Request与Response
        InputStream in = client.getInputStream();
        OutputStream out = client.getOutputStream();
        // 注:这里要明白,每次请求的Request和Response都是不同的(因为连接时的socket不同),他俩的作用域仅为当前会话
        TomcatRequest request = new TomcatRequest(in);
        TomcatResponse response = new TomcatResponse(out);

        // 获取请求URL,寻找相应Servlet进行处理
        String url = request.getUrl();
        // 2.判断改url是否有对应的Servlet实例
        if (servletMapping.containsKey(url)) {
            // 如果有,调用service方法进行处理
            servletMapping.get(url).service(request, response);
        }
        else {
            // 如果没有,写出404
            response.write("404 - Not Found");
        }

        // 3.关闭本次连接相关资源
        out.flush();
        out.close();
        in.close();
        client.close();
    }

    public static void main(String[] args) {
        new SimpleTomcat().start();
    }
}

servlet.FirstServlet

package com.sample.servlet;

import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;

import java.io.IOException;

public class FirstServlet extends TomcatServlet {

    @Override
    protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
        tomcatResponse.write("this is FirstServlet!");
    }

    @Override
    protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
        doPost(tomcatRequest, tomcatResponse);
    }
}

servlet.SecondServlet

SecondServlet.java

package com.sample.servlet;


import com.sample.http.TomcatRequest;
import com.sample.http.TomcatResponse;
import com.sample.http.TomcatServlet;

import java.io.IOException;

public class SecondServlet extends TomcatServlet {

    @Override
    protected void doPost(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
        tomcatResponse.write("Hello world!");
    }

    @Override
    protected void doGet(TomcatRequest tomcatRequest, TomcatResponse tomcatResponse) throws IOException {
        doPost(tomcatRequest, tomcatResponse);
    }
}

配置

web.properties

servlet.one.url=/firstServlet
servlet.one.className=com.sample.servlet.FirstServlet

servlet.two.url=/secondServlet
servlet.two.className=com.sample.servlet.SecondServlet

文件结构

在这里插入图片描述

运行

运行我们的 SimpleTomcat.java
在这里插入图片描述

在浏览器中输入

http://localhost:8080/firstServlet

或者

http://localhost:8080/secondServlet

可以看到

在这里插入图片描述

参考

https://blog.csdn.net/weixin_43935927/article/details/108743213

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【手写一个Tomcat】SimpleTomcat-01 的相关文章

  • Hibernate注解放置问题

    我有一个我认为很简单的问题 我见过两种方式的例子 问题是 为什么我不能将注释放在字段上 让我举一个例子 Entity Table name widget public class Widget private Integer id Id G
  • 如何在android上的python kivy中关闭应用程序后使服务继续工作

    我希望我的服务在关闭应用程序后继续工作 但我做不到 我听说我应该使用startForeground 但如何在Python中做到这一点呢 应用程序代码 from kivy app import App from kivy uix floatl
  • SAML 服务提供商 Spring Security

    当使用预先配置的服务提供者元数据时 在 Spring Security 中 是否应该有 2 个用于扩展元数据委托的 bean 定义 一份用于 IDP 元数据 一份用于 SP 元数据
  • Java:如何从转义的 URL 获取文件?

    我收到了一个定位本地文件的 URL 事实上我收到的 URL 不在我的控制范围内 URL 按照 RFC2396 中的定义进行有效转义 如何将其转换为 Java File 对象 有趣的是 URL getFile 方法返回一个字符串 而不是文件
  • java中如何连接字符串

    这是我的字符串连接代码 StringSecret java public class StringSecret public static void main String args String s new String abc s co
  • 如何在不超过最大值的情况下增加变量?

    我正在为学校开发一个简单的视频游戏程序 我创建了一个方法 如果调用该方法 玩家将获得 15 点生命值 我必须将生命值保持在最大值 100 并且由于我目前的编程能力有限 我正在做这样的事情 public void getHealed if h
  • Cassandra java驱动程序协议版本和连接限制不匹配

    我使用的java驱动程序版本 2 1 4卡桑德拉版本 dsc cassandra 2 1 10cql 的输出给出以下内容 cqlsh 5 0 1 Cassandra 2 1 10 CQL spec 3 2 1 Native protocol
  • 如何模拟从抽象类继承的受保护子类方法?

    如何使用 Mockito 或 PowerMock 模拟由子类实现但从抽象超类继承的受保护方法 换句话说 我想在模拟 doSomethingElse 的同时测试 doSomething 方法 抽象超类 public abstract clas
  • 在 Netbeans 8 上配置 JBoss EAP 的问题

    我已经下载了 JBoss EAP 7 并正在 Netbeans 8 上配置它 我已经到达向导 实例属性 其中要求从选择框中选择 域 当我打开选择框时 它是空的 没有什么可以选择的 因此 完成 按钮也处于非活动状态 这使得无法完成配置 我通过
  • 具有 java XSLT 扩展的数组

    我正在尝试使用 java 在 XSLT 扩展中使用数组 我收到以下错误 Caused by java lang ClassCastException org apache xpath objects XObject cannot be ca
  • 将 SignedHash 插入 PDF 中以进行外部签名过程 -workingSample

    遵循电子书第 4 3 3 节 PDF 文档的数字签名 https jira nuxeo com secure attachment 49931 digitalsignatures20130304 pdf 我正在尝试创建一个工作示例 其中 客
  • 使用 SQLITE 按最近的纬度和经度坐标排序

    我必须获得一个 SQLite SQL 语句 以便在给定初始位置的情况下按最近的纬度和经度坐标进行排序 这是我在 sqlite 数据库中的表的例句 SELECT id name lat lng FROM items EXAMPLE RESUL
  • 普罗米修斯指标 - 未找到

    我有 Spring Boot 应用程序 并且正在使用 vertx 我想监控服务和 jvm 为此我选择了 Prometheus 这是我的监控配置类 Configuration public class MonitoringConfig Bea
  • 如何在selenium服务器上提供自定义功能?

    我知道可以通过某种方法获得一些硒功能 其中之一如下 driver getCapabilities getBrowserName 它返回浏览器名称的值 但如果它指的是一个可用的方法 如果我没有误解的话 这似乎与自定义功能有关 就像我的意思是
  • 我可以创建自定义 java.* 包吗?

    我可以创建一个与预定义包同名的自己的包吗在Java中 比如java lang 如果是这样 结果会怎样 这难道不能让我访问该包的受保护的成员 如果不是 是什么阻止我这样做 No java lang被禁止 安全管理器不允许 自定义 类java
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • Eclipse 中 Spring MVC 模型对象的 (jsp /jstl) 视图中的代码辅助

    在 Spring MVC 中 当将对象放置在视图模型中时 如下所示 public String getUser Model model fetch user model addAttribute user user return viewN
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu

随机推荐

  • Spring Boot中使用WebSocket 【第一部分】

    简介 所谓WebSocket 类似于Socket 它的作用是可以让Web应用中的客户端和服务端建立全双工通信 在基于Spring的应用中使用WebSocket一般可以有以下三种方式 使用Java提供的 ServerEndpoint注解实现
  • 关于maven项目中Tomcat10与JSTL问题汇总(Debug亲身经历)

    文章目录 问题描述与解决方法 问题1 无法在web xml或使用此应用程序部署的jar文件中解析绝对uri http java sun com jsp jstl core 问题2 java lang NoClassDefFoundError
  • java 调用cmd_java打开本地应用程序(调用cmd)---Runtime用法详解

    有时候我们需要借助java程序打开电脑自带的一些程序 可以直接打开或者借助cmd命令窗口打开一些常用的应用程序或者脚本 在cmd窗口执行的命令都可以通过这种方式运行 例如 packagecn xm exam test importjava
  • 爱情与婚姻的区别(多种说法)

    在生活中 爱情与婚姻的话题 总是被人们不停的讨论着 有人说婚姻是爱情的坟墓 也有人说婚姻是爱情的延续 可我却认为 爱情是浪漫的 而婚姻是现实的 在现实中没有一个人敢说自己真正的懂得爱情 或是看透了婚姻 而至于婚姻是爱情的延续也好 或婚姻是爱
  • html5 css3教案,认识CSS3和HTML5

    内容简介 认识CSS3和HTML5 内容节选 共100页 本文写于2006年1月 当时IE7 IE8和Firefox3还未发行 文中所有说的浏览器支持均未包括这三个版本的浏览器 在IE8和Firefox3中 文中的大部分选择符已经被支持 d
  • android 实现GridView多选效果

    在使用 GridView的时候 有时需要多选上面显示的类容 比如批量删除上面显示的图片 批量上传图片等 这个时候我们可以使用层叠图来实现 效果如下 点击图片后 上面会显示出一个打钩的图片 如图 再点击选中图片 勾消失 转存失败重新上传取消
  • 基于均匀分布总体的样本及其概率密度函数的数据分析

    基于均匀分布总体的样本及其概率密度函数的数据分析 在数据分析中 我们经常遇到从某个总体中获取的样本数据 本文将讨论一个由均匀分布总体生成的样本 并使用R语言来演示相关的数据分析技术 首先 让我们了解均匀分布总体及其概率密度函数 均匀分布是一
  • 动态数据锚点定位

    锚点定位 左侧点击右侧锚点定位样式一 div class left a href book time a a href search a a href search flow a div div class right div div di
  • Java算法LC刷(3.无重复字符的最长子串)

    无重复字符的最长子串 package com core doc LeetCode public class T3 public static void main String args int map lengthOfLongestSubs
  • DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead

    问题描述 使用find element by id时报错 DeprecationWarning find element by commands are deprecated Please use find element instead
  • php组件缓冲区溢出漏洞,一个缓冲区溢出漏洞的简易教程

    这篇文章类似于 傻瓜系列之利用缓冲区溢出 在这类漏洞中 我们的做法是利用网络 程序控制器 输入等等 发送超大的数据缓冲区给程序 覆盖程序内存的重要部分 在这些缓冲区覆盖程序内存之后 我们可以重定向程序的执行流并运行注入代码 首先 我们需要做
  • 财务用计算机在成本核销,《初级会计电算化》精选题库附答案解析(五)

    初级会计电算化 练习题五 含答案解析 1 当月的记账凭证必须全部记账 如有未记账的当月凭证 系统将 TopSage A 不能结账 B 继续结账 C 放到下月 答案 A 2 往来账管理的往来客户档案的设置其最主要内容是 TopSage A 客
  • 关于 IDEA创建Spring项目时出现Cannot resolve plugin XXX的解决办法

    在创建spring项目时候Plugins文件夹下面出现许多的红色波浪报错 如下 org apache maven plugins maven clean plugin 2 5 org apache maven plugins maven c
  • 前端 - js - 编程题23 - 两数之和

    给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那 两个 整数 并返回他们的数组下标 你可以假设每种输入只会对应一个答案 但是 你不能重复利用这个数组中同样的元素 示例 给定nums 2 7 11 15
  • Windows系统如何查看占用的端口

    Windows系统如何查看占用的端口 在有些时候 我们需要知道Windows系统占用了那些端口 比如 需要在服务器上部署一个应用程序时 需要给这个应用程序提供空闲端口用来通信 除了一些约定俗成的端口外 例如 HTTP服务占用80端口 FTP
  • Java多线程列子

    问题如下 两个线程 线程A 线程B 一个固定容量为50的队列List
  • 解决Spring boot 工程console打印太多信息

    resources下添加配置文件 logback xml 添加下面代码
  • 决策树(Decision Tree)

    一 简介 决策树 decision tree 是一种基本的分类与回归方法 本章主要讨论用于分类的决策树 决策树模型呈树形结构 在分类问题中 表示基于特征对实例进行分类的过程 它可以认为是if then规则的集合 也可以认为是定义在特征空间与
  • SpringBoot实战——个人博客项目

    目录 一 项目简介 二 项目整体架构 数据库模块 后端模块 前端模块 三 项目具体展示 四 项目的具体实现 1 一些准备工作 数据库 数据表的创建 设置数据库和MyBatis的配置 将前端项目引入到当前项目中 2 登录注册模块 实体类的创建
  • 【手写一个Tomcat】SimpleTomcat-01

    目录 前言 实现 http TomcatRequest http TomcatResponse http TomcatServlet SimpleTomcat servlet FirstServlet servlet SecondServl