前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >基于传统I/O手写Tomcat

基于传统I/O手写Tomcat

作者头像
Tom弹架构
发布于 2022-02-11 01:28:29
发布于 2022-02-11 01:28:29
38400
代码可运行
举报
文章被收录于专栏:Tom弹架构Tom弹架构
运行总次数:0
代码可运行

本文节选自《Netty 4核心原理》

我们知道,Tomcat是基于J2EE规范的Web容器,主要入口是web.xml文件。web.xml文件中主要配置Servlet、Filter、Listener等,而Servlet、Filter、Listener在J2EE中只是抽象的实现,具体业务逻辑由开发者来实现。本章内容,就以最常用的Servlet为例来详细展开。

1 环境准备

1.1 定义GPServlet抽象类

首先,我们创建GPServlet类。我们都知道GPServlet生命周期中最常用的方法是doGet()方法和doPost()方法,而doGet()方法和doPost()方法是service()方法的分支实现,看下面的简易版Servlet源码实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

package com.tom.tomcat.http;

public abstract class GPServlet {

    public void service(GPRequest request,GPResponse response) throws Exception{

        //由service()方法决定是调用doGet()还是调用doPost()
        if("GET".equalsIgnoreCase(request.getMethod())){
            doGet(request, response);
        }else{
            doPost(request, response);
        }

    }

    public abstract void doGet(GPRequest request,GPResponse response) throws Exception;

    public abstract void doPost(GPRequest request,GPResponse response) throws Exception;

}

从上面的代码中,我们看到,doGet()方法和doPost()方法中有两个参数GPRequest和GPResponse对象,这两个对象是由Web容器创建的,主要是对底层Socket的输入输出的封装。其中GPRequest是对Input的封装,GPResponse是对Output的封装。

1.2 创建用户业务代码

下面基于GPServlet来实现两个业务逻辑FirstServlet和SecondServlet。FirstServlet类的实现代码如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

package com.tom.tomcat.servlet;

import com.tom.tomcat.http.GPRequest;
import com.tom.tomcat.http.GPResponse;
import com.tom.tomcat.http.GPServlet;

public class FirstServlet extends GPServlet {

    public void doGet(GPRequest request, GPResponse response) throws Exception {
        this.doPost(request, response);
    }

    public void doPost(GPRequest request, GPResponse response) throws Exception {
        response.write("This is First Servlet");
    }

}
SecondServlet类的实现代码如下。
package com.tom.tomcat.servlet;

import com.tom.tomcat.http.GPRequest;
import com.tom.tomcat.http.GPResponse;
import com.tom.tomcat.http.GPServlet;

public class SecondServlet extends GPServlet {

    public void doGet(GPRequest request, GPResponse response) throws Exception {
        this.doPost(request, response);
    }

    public void doPost(GPRequest request, GPResponse response) throws Exception {
        response.write("This is Second Servlet");
    }

}

1.3 完成web.properties配置

为了简化操作,我们用web.properties文件代替web.xml文件,具体内容如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

servlet.one.url=/firstServlet.do
servlet.one.className=com.tom.tomcat.servlet.FirstServlet

servlet.two.url=/secondServlet.do
servlet.two.className=com.tom.tomcat.servlet.SecondServlet

上述代码分别给两个Servlet配置了/firstServlet.do和/secondServlet.do的URL映射。

2 基于传统I/O手写Tomcat

下面我们来看GPRequest和GPResponse的基本实现。

2.1 创建GPRequest对象

GPRequest主要就是对HTTP的请求头信息进行解析。我们从浏览器发送一个HTTP请求,如在浏览器地址栏中输入 http://localhost:8080 ,后台服务器获取的请求其实就是一串字符串,具体格式如下。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

在GPRequest获得输入内容之后,对这一串满足HTTP的字符信息进行解析。我们来看GPRequest简单直接的代码实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

package com.tom.tomcat.http;

import java.io.InputStream;

/**
 * Created by Tom.
 */
public class GPRequest {

    private String method;
    private String url;

    public GPRequest(InputStream in){
        try {
            //获取HTTP内容
            String content = "";
            byte[] buff = new byte[1024];
            int len = 0;
            if ((len = in.read(buff)) > 0) {
                content = new String(buff,0,len);
            }

            String line = content.split("\\n")[0];
            String [] arr = line.split("\\s");

            this.method = arr[0];
            this.url = arr[1].split("\\?")[0];
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public String getUrl() {
        return url;
    }

    public String getMethod() {
        return method;
    }
}

在上面的代码中,GPRequest主要提供了getUrl()方法和getMethod()方法。输入流InputStream作为GPRequest的构造参数传入,在构造函数中,用字符串切割的方法提取请求方式和URL。

2.2 创建GPResponse对象

接下来看GPResponse的实现,与GPRequest的实现思路类似,就是按照HTTP规范从Output输出格式化的字符串,具体格式如下:

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

HTTP/1.1 200 OK
Server: Tomcat
Location: http://localhost:8080
Connection: Keep-Alive
Content-Type: text/html;

This My Servlet

下面来看代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

package com.tom.tomcat.http;

import java.io.OutputStream;

/**
 * Created by Tom.
 */
public class GPResponse {
    private OutputStream out;
    public GPResponse(OutputStream out){
        this.out = out;
    }

    public void write(String s) throws Exception {
        //输出也要遵循HTTP
        //状态码为200
        StringBuilder sb = new StringBuilder();
                sb.append("HTTP/1.1 200 OK\n")
                .append("Content-Type: text/html;\n")
                .append("\r\n")
                .append(s);
        out.write(sb.toString().getBytes());
    }
}

上面的代码中,输出流OutputStream作为GPResponse的构造参数传入,主要提供了一个write()方法。通过write()方法按照HTTP规范输出字符串。

2.3 创建GPTomcat启动类

前面2.1和2.2两节只是对J2EE规范的再现,接下来就是真正Web容器的实现逻辑,分为三个阶段:初始化阶段、服务就绪阶段、接受请求阶段。第一阶段:初始化阶段,主要是完成对web.xml文件的解析。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

package com.tom.tomcat;

import com.tom.tomcat.http.GPRequest;
import com.tom.tomcat.http.GPResponse;
import com.tom.tomcat.http.GPServlet;

import java.io.FileInputStream;
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;

/**
 * Created by Tom.
 */
public class GPTomcat {
    private int port = 8080;
    private ServerSocket server;
    private Map<String,GPServlet> servletMapping = new HashMap<String,GPServlet>();

    private Properties webxml = new Properties();


    private void init(){

        //加载web.xml文件,同时初始化ServletMapping对象
        try{
            String WEB_INF = this.getClass().getResource("/").getPath();
            FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");

            webxml.load(fis);

            for (Object k : webxml.keySet()) {

                String key = k.toString();
                if(key.endsWith(".url")){
                    String servletName = key.replaceAll("\\.url$", "");
                    String url = webxml.getProperty(key);
                    String className = webxml.getProperty(servletName + ".className");
                    //单实例,多线程
                    GPServlet obj = (GPServlet)Class.forName(className).newInstance();
                    servletMapping.put(url, obj);
                }

            }


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

    }

}

上面代码中,首先从WEB-INF读取web.properties文件并对其进行解析,然后将URL规则和GPServlet的对应关系保存到servletMapping中。第二阶段:服务就绪阶段,完成ServerSocket的准备工作。在GPTomcat类中增加start()方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

    public void start(){

        //1.加载配置文件,初始化ServletMapping
        init();

        try {
            server = new ServerSocket(this.port);

            System.out.println("GPTomcat已启动,监听的端口是:" + this.port);

            //2.等待用户请求,用一个死循环来等待用户请求
            while (true) {
                Socket client = server.accept();
                //3.HTTP请求,发送的数据就是字符串——有规律的字符串(HTTP)
                process(client);

            }

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

第三阶段:接受请求阶段,完成每一次请求的处理。在GPTomcat中增加process()方法的实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

    private void process(Socket client) throws Exception {

        InputStream is = client.getInputStream();
        OutputStream os = client.getOutputStream();

        //4.Request(InputStrean)/Response(OutputStrean)
        GPRequest request = new GPRequest(is);
        GPResponse response = new GPResponse(os);

        //5.从协议内容中获得URL,把相应的Servlet用反射进行实例化
        String url = request.getUrl();

        if(servletMapping.containsKey(url)){
            //6.调用实例化对象的service()方法,执行具体的逻辑doGet()/doPost()方法
            servletMapping.get(url).service(request,response);
        }else{
            response.write("404 - Not Found");
        }


        os.flush();
        os.close();

        is.close();
        client.close();
    }

每次客户端请求过来以后,从servletMapping中获取其对应的Servlet对象,同时实例化GPRequest和GPResponse对象,将GPRequest和GPResponse对象作为参数传入service()方法,最终执行业务逻辑。最后,增加main()方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

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

服务启动后,运行效果如下图所示。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Tom弹架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
手写tomcat+servlet
写程序一定要有思路,思路很重要! 一、我们分两步第一步先实现手写tomcat,第二部写servlet 所用技术: 1、soket通信 IO流 2、http请求与相应 3、解析xml 4、java反射技术 导入所需要的jar: <dependencies> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <arti
故久
2019/09/29
4540
手写tomcat+servlet
惊呆了!手写4个mini版的tomcat!
Apache Tomcat 是Java Servlet, JavaServer Pages (JSP),Java表达式语言和Java的WebSocket技术的一个开源实现 ,通常我们将Tomcat称为Web容器或者Servlet容器 。
田维常
2021/04/02
4990
Tomcat 到底干了啥
此文为Tomcat系列的第一篇,Tomcat的整体架构个人感觉非常有意思,本文我们先非常简单的入个门。
全栈程序员站长
2022/09/14
3430
Tomcat 到底干了啥
手写Tomcat
笔者称自己手写的Tomcat为盗版,反之则为正版。在手写简易版Tomcat之前,我们来看看如何使用正版的Tomcat
晚上没宵夜
2020/03/10
6010
tomcat和servlet快速入门教程!!!
4.1 bin/startup.bat ,双击运行该文件即可 4.2 访问:浏览器输入: http://localhost:8080 回车访问自己 或者 http://别人的ip:8080 访问别人 可以通过ipconfig:查看本机ip地址
大忽悠爱学习
2021/11/15
4520
手写一个基于NIO的迷你版Tomcat
在很久之前看到了一篇文章写一个迷你版的Tomcat,觉得还是很有意思的,于是也跟着手敲了一遍,果不其然得出了想要的hello world,但是他这个是基于BIO的,正好最近看了并发编程的书,于是尝试将这位大佬的代码改一改,于是就有了这个基于NIO的迷你Tomcat。
beifengtz
2019/06/03
6430
手写一个基于NIO的迷你版Tomcat
什么是Servlet(原理,从访问到方法)
Servlet是SUN公司提供的一门用于开发动态WEB资源的技术。SUN公司在其API中提供了一个Servlet接口,用户若想开发一个动态WEB资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
云扬四海
2019/06/05
1.4K0
什么是Servlet(原理,从访问到方法)
JavaWeb-过滤器Filter学习(五)全站压缩
全站压缩,最大的好久就是帮客户端节省流量。 数据压缩,我们需要用到二个Java类,也就是java.util.zip 中的 类 GZIPOutputStream 此类为使用 GZIP 文件格式写入压缩数据实现流过滤器。
谙忆
2021/01/21
4690
JavaWeb-过滤器Filter学习(五)全站压缩
java学习与应用(4.4)--Tomcat、servlet等
常用的web服务器软件:webLogic(oracle),webSphere(IBM),JBOSS(JBOSS公司)(以上三种支持所有JavaEE规范,企业版共13项规范),Tomcat(Apache基金,支持少量JavaEE规范) Tomcat:下载,解压使用。其中webapps存放网页,work存放运行数据,bin存放执行文件(shutdown.bat可关闭)。没有配置JAVA_HOME可能不能启动startup.bat。logs中存放日志信息记录一些错误等。netstat -ano查看端口使用的PID。conf配置目录的server.xml定义了tomcat的端口使用。 将代码打包为war包,放置到webapps下,会自动解压缩,和自动删除。server.xml的host标签下,定义Context自闭和标签中,定义docBase项目路径和path虚拟目录的属性便于访问。 常用的热部署并防止修改错误server.xml内容:方式为:conf下Catania下localhost下创建任意名称的xml文件,写入Context标签和属性,替换server.xml中的路径部署。 ROOT为项目根目录,WEB_INF为动态项目目录(web.xml为核心配置文件,classes目录存放字节码,lib存放jar包),其他为静态目录文件。 run->configuration ->Tomcat Server ->local->Application server中将tomcat集成到idea中。Java Enerprise -> Web Application,create server.xml勾选。等等配置。 idea直接修改可以使用热部署,不用重启服务器,进行代码调试,文件创建。idea也可以修改虚拟路径,方便使用。
嘘、小点声
2020/02/23
4360
写一个迷你版的Tomcat前言Write MyTomcat
Tomcat,这只3脚猫,大学的时候就认识了,直到现在工作中,也常会和它打交道。这是一只神奇的猫,今天让我来抽象你,实现你!
用户2890438
2018/08/21
4620
写一个迷你版的Tomcat前言Write MyTomcat
Web---创建Servlet的3种方式、简单的用户注册功能
创建Servlet的方式,在上篇博客中,已经用了方式1(实现Servlet接口),接下来本节讲的是另外2种方式。 上篇博客地址:http://blog.csdn.net/qq_26525215/article/details/51942252
谙忆
2021/01/21
3640
Web---创建Servlet的3种方式、简单的用户注册功能
Tomcat与Servlet——浅入
本篇文章整理自我的CSDN,是我以前学习的时候总结的,当时使用的IDE是MyEclipse,内容没什么问题,可能过于书面化,等我整理完毕我的CSDN相关内容后,会开始更新进阶内容。
东边的大西瓜
2022/05/05
5680
Tomcat与Servlet——浅入
Spring Boot整合Servlet,Filter,Listener,访问静态资源
注意:在启动类上需要加上@ServletComponentScan注解 意思是:在启动时扫描@WebServlet注解 ,创建Servlet的实例
JokerDJ
2023/11/27
2310
Spring Boot整合Servlet,Filter,Listener,访问静态资源
hibernate 在tomcat7.X 下配置mysql数据源「建议收藏」
今天刚看到hibernate,发如今hibernate配置数据源时网上的资料都太久远了,一般以tomcat 5 版本号下的配置居多。而tomcat 7下的配置略有变化,新手找资料困难,可能会略受打击,故整理资料与大家共享。也可作备忘之用。若有不当之处。还请指教!
全栈程序员站长
2022/07/07
5510
手写个Tomcat雏型
上面只是个简易版的Tomcat雏型,可大致了解到Tomcat的流程,Tomcat无非就是 在基础版本上添加了各种Servlet、Request、Response、Session、Cookie、ServletContext、ServletConfig、EL、JSTL、Filter、Listener、JSP等这些东西,有空再写两章Tomcat源码底层的东西。
sowhat1412
2020/11/05
4530
手写个Tomcat雏型
手写一个简化版Tomcat
      Tomcat作为Web服务器深受市场欢迎,有必要对其进行深入的研究。在工作中,我们经常会把写好的代码打包放在Tomcat里并启动,然后在浏览器里就能愉快的调用我们写的代码来实现相应的功能了
我叫刘半仙
2018/04/16
9520
手写一个简化版Tomcat
Tomcat Servlet编程基础
之前一直使用的是Apache服务器,对于Tomcat的工作方式反而不那么习惯。给我一个Java web程序源码我都不晓得他应该怎么连接到Tomcat上。于是趁着无聊的时候看了个J2EE的Servlet教程,终于对怎么把java部署到Tomcat上、以及Tomcat的基本工作流程有了简要的认识。当然,这里仅仅指的是简单的Servlet程序的部署方法,不过这点方法从理论上讲已经具备了写一个简单web 应用的能力了。
mythsman
2022/11/14
2490
Servlet入门笔记
J2EE(Java 2 Platform Enterprise Edition)是指“Java 2 企业版”
Breeze.
2022/07/12
4020
Servlet入门笔记
java安全之结合CC链注入无文件Tomcat内存马
Tomcat内存马基础可以看我的上一篇:https://www.0kai0.cn/?p=240 前言-fliter等内存马局限 具体新建servlet的过程: https://blog.csdn.ne
亿人安全
2023/02/28
1.1K0
java安全之结合CC链注入无文件Tomcat内存马
创建Servlet的几种方式+web.xml中关于servlet的一些配置+浏览器、服务器交互Postman测试
直接输入myServlet2,会访问doGet方法 直接输入Demo1.htlm,店家按钮,会访问doPost方法
2020/10/23
1.5K0
创建Servlet的几种方式+web.xml中关于servlet的一些配置+浏览器、服务器交互Postman测试
相关推荐
手写tomcat+servlet
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文