手动实现一个简易版 tomcat(yet)

https://mp.weixin.qq.com/s?__biz=MzA5MzcxNjY4Ng==&mid=2648107477&idx=1&sn=237afdcd8dc67f3a36aac8a38fcaada9&chksm=887be074bf0c69627659d3f076420ba7d477bc08405437eafe4d99ed22e6d2f9a53c80a73d87&mpshare=1&scene=1&srcid=0606qoAMsGtii5eGzil3h35x&key=abc5240e014eede13ee3b36db10a8651176fd1ff480250a3b2660676db6e3a4f4c0857207a64f65dbafc7c5276b2d8e8375d9a65c25572ed6df33fa45995f85c0fc5588801f2def497593ae43d6347d5&ascene=0&uin=MTA2NzUxMDAyNQ%3D%3D&devicetype=iMac+MacBookAir6%2C2+OSX+OSX+10.10.5+build(14F2511)&version=11020012&lang=zh_CN&pass_ticket=mxEo2xKCtH9iR2PLbxrAcJSXJ7pfbLGSU5PqNtNoXLtIgAf3or95FesHIZkUX27e

tomcat简介

Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

2手动实现

项目结构如下图所示

接下来我们就开始一步步实现,有servlet开发经验的对于Request和Response一定不陌生,所以就先实现自己的Request和Response。首先实现Response如下所示。

package com.duomeng.tomcat;import java.io.IOException;import java.io.InputStream;/** * */public class MyRequest {    private String url;    private String method;    public MyRequest(InputStream inputStream) throws IOException {        StringBuilder httpRequest = new StringBuilder();        byte[] httpRequestByte = new byte[1014];        int length = 0;        if ((length = inputStream.read(httpRequestByte)) > 0) {            httpRequest.append(new String(httpRequestByte,0,length));        }        System.out.println("httpRequest = [" + httpRequest + "]");        /**        GET /student HTTP/1.1        Host: localhost:8080        Connection: keep-alive        Cache-Control: max-age=0        Upgrade-Insecure-Requests: 1        User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,         */        String httpHead = httpRequest.toString().split("\n")[0];        url = httpHead.split("\\s")[1];        method = httpHead.split("\\s")[0];        System.out.println("MyRequests = [" + this + "]");    }    public String getUrl() {        return url;    }    public void setUrl(String url) {        this.url = url;    }    public String getMethod() {        return method;    }    public void setMethod(String method) {        this.method = method;    }}

接着来实现自己的Reponse,如下图所示。

package com.duomeng.tomcat;import java.io.IOException;import java.io.OutputStream;public class MyResponse {    private OutputStream outputStream;    public MyResponse(OutputStream outputStream) {        this.outputStream = outputStream;    }    public  void write(String content) throws IOException {        /**         * HTTP/1.1 200 OK         Content-type:text/html         */        StringBuffer stringBuffer = new StringBuffer();        stringBuffer.append("HTTP/1.1 200 OK\n")                .append("Content-type:text/html\n")                .append("\r\n")                .append("<html><head><title>Hello World</title></head><body>")                .append(content)                .append("</body><html>");        outputStream.write(stringBuffer .toString().getBytes());        outputStream.close();    }}

有了自己的请求信息和响应信息,那接下来就要写servlet了。通常写自己的Servlet的时候要继承一个Servlet的父类HttpServlet,所以首先要写一个Servlet的父类,代码如下。

package com.duomeng.tomcat;public abstract class MyServlet {    protected abstract void doGet(MyRequest request,MyResponse response);    protected abstract void doPost(MyRequest request,MyResponse response);    public void service(MyRequest request,MyResponse response) throws NoSuchMethodException {        if (request.getMethod().equalsIgnoreCase("POST")) {            doPost(request,response);        }else if(request.getMethod().equalsIgnoreCase("GET")){            doGet(request,response);        }else {            throw new NoSuchMethodException("not support");        }    }}

那么接下来就要写自己业务相关的Servlet,这里写一个和学生相关的Servlet,叫StudentServlet,还有一个和老师相关的Servlet,叫TeacherServlet。具体的代码分别如下。

package com.duomeng.tomcat;import java.io.IOException;public class StudentServlet extends MyServlet{    @Override    protected void doGet(MyRequest request, MyResponse response) {        try {            response.write("I am a student.");        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    protected void doPost(MyRequest request, MyResponse response) {        try {            response.write("I am a student.");        } catch (IOException e) {            e.printStackTrace();        }    }}

和老师相关的Servlet如下:

package com.duomeng.tomcat;import java.io.IOException;public class TeacherServlet extends MyServlet{    @Override    protected void doGet(MyRequest request, MyResponse response) {        try {            response.write("I am a treacher.");        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    protected void doPost(MyRequest request, MyResponse response) {        try {            response.write("I am a treacher.");        } catch (IOException e) {            e.printStackTrace();        }    }}

因为浏览器在请求的时候还会请求浏览器上面显示的favicon.ico,所以要编写一个相关的servlet来处理请求,代码如下。

package com.duomeng.tomcat;import java.io.IOException;public class FaviconServlet extends MyServlet{    @Override    protected void doGet(MyRequest request, MyResponse response) {        try {            response.write("Favicon");        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    protected void doPost(MyRequest request, MyResponse response) {        try {            response.write("Favicon");        } catch (IOException e) {            e.printStackTrace();        }    }}

以上就是写的跟具体实际当中业务相关的代码,还记得在Servlet的时候要在项目web.xml中配置请求地址和具体的servlet的映射关系,所以此处要手动来实现一个映射的关系。具体代码如下。

package com.duomeng.tomcat;public class ServletMapping {    private String servletName;    private String url;    private String clazz;    public ServletMapping(String servletName, String url, String clazz) {        this.servletName = servletName;        this.url = url;        this.clazz = clazz;    }    public String getServletName() {        return servletName;    }    public void setServletName(String servletName) {        this.servletName = servletName;    }    public String getUrl() {        return url;    }    public void setUrl(String url) {        this.url = url;    }    public String getClazz() {        return clazz;    }    public void setClazz(String clazz) {        this.clazz = clazz;    }}

以上是每个具体的Servlet的信息,接下来要写一个存储有映射关系的类,代码如下。

package com.duomeng.tomcat;import java.util.ArrayList;import java.util.List;public class ServletMappingConfig {    public static List<ServletMapping> servletMappings = new ArrayList<ServletMapping>(16);    static {        servletMappings.add(new ServletMapping("student","/student","com.duomeng.tomcat.StudentServlet"));        servletMappings.add(new ServletMapping("teacher","/teacher","com.duomeng.tomcat.TeacherServlet"));        servletMappings.add(new ServletMapping("favicon","/favicon.ico","com.duomeng.tomcat.FaviconServlet"));    }}

接下来就是真正的主角登场了,那就是对于请求做分发处理的相关逻辑了,具体代码如下所示。

package com.duomeng.tomcat;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;public class MyTomcat {    private int port = 8080;    private Map<String,String> urlServletMap = new HashMap<String, String>(16);    public MyTomcat(int port) {        this.port = port;    }    public void start(){        //初始化请求映射关系        initServletMapping();        ServerSocket serverSocket = null;        try {            serverSocket = new ServerSocket(port);            System.out.println("My tomcat begin start");            while (true){                Socket socket = serverSocket.accept();                InputStream inputStream = socket.getInputStream();                OutputStream outputStream = socket.getOutputStream();                MyRequest request = new MyRequest(inputStream);                MyResponse response = new MyResponse(outputStream);                //分发请求                dispatch(request,response);                socket.close();            }        } catch (IOException e) {            e.printStackTrace();        }    }    private void initServletMapping(){        for (ServletMapping servletMapping:ServletMappingConfig.servletMappings) {            urlServletMap.put(servletMapping.getUrl(),servletMapping.getClazz());        }    }    private void dispatch(MyRequest request,MyResponse response){        String clazz = urlServletMap.get(request.getUrl());        try {            Class servletClass = Class.forName(clazz);            MyServlet myServlet = (MyServlet)servletClass.newInstance();            myServlet.service(request,response);        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (NoSuchMethodException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        new MyTomcat(8080).start();    }}

如果没有问题的话,启动main函数之后,在浏览器发起具体的请求会看到如下显示内容。

原文地址:https://www.cnblogs.com/silyvin/p/9164072.html

时间: 2024-10-08 03:04:06

手动实现一个简易版 tomcat(yet)的相关文章

手写一个简易版Tomcat

前言 Tomcat Write MyTomcat Tomcat是非常流行的Web Server,它还是一个满足Servlet规范的容器.那么想一想,Tomcat和我们的Web应用是什么关系? 从感性上来说,我们一般需要把Web应用打成WAR包部署到Tomcat中,在我们的Web应用中,我们要指明URL被哪个类的哪个方法所处理(不论是原始的Servlet开发,还是现在流行的Spring MVC都必须指明). 由于我们的Web应用是运行在Tomcat中,那么显然,请求必定是先到达Tomcat的.To

【react】---手动封装一个简易版的redux---【韶华】

export let createStore = (reducer)=>{ //定义默认的state let state; //定义默认的action let actionTypes = "@@redux/INIT"+Math.random(); let initAction = {type:actionTypes} //将所以需要监听的函数放在这个里面 let listeners = [] //定义getState函数 let getState = ()=>state;

依赖注入[4]: 创建一个简易版的DI框架[上篇]

本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章(<控制反转>.<基于IoC的设计模式>和< 依赖注入模式>)从纯理论的角度对依赖注入进行了深入论述,为了让读者朋友能够更好地理解.NET Core的依赖注入框架的设计思想和实现原理,我们创建了一个简易版本的DI框架,也就是我们在前面文章中多次提及的Cat.我们会上下两篇来介绍这个被称为为Cat的DI框架,上篇介绍编程模型,下篇关注设计实现.[源代码从这里下载] 目录一.DI容器的层

依赖注入[5]: 创建一个简易版的DI框架[下篇]

为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]>中我们介绍了Cat的基本编程模式,接下来我们就来聊聊Cat的设计和实现. 目录一.服务注册:ServiceRegistry 二.DI容器:Cat 三.扩展方法 一.服务注册:ServiceRegistry 由于作为DI容器的Cat对象总是利用预先添加到服务注册来提供对应的服务实例,所以服务注册至关重要.如下

一个简易版的Function.prototype.bind实现

重新看<JavaScript设计模式与开发实践>一书,第32页发现个简易版的Function.prototype.bind实现,非常容易理解,记录在这了. Function.prototype.bind = function (context) { var self = this; return function () { return self.apply(context, arguments); }; }; var obj = { name: 'sven' }; var func = fu

实现一个简易版webpack

现实 webpack 的打包产物 大概长这样(只把核心代码留下来): 实现一个简版的webpack 依葫芦画瓢,实现思路分2步: 1. 分析入口文件,把所有的依赖找出来(包括所有后代的依赖) 2. 拼接出类似上面的立即执行函数 找依赖 const fs = require('fs'); const path = require('path'); const parser = require('@babel/parser'); const traverse = require('@babel/tr

使用 LinkedBlockingQueue 实现简易版线程池

前一阵子在做联系人的导入功能,使用POI组件解析Excel文件后获取到联系人列表,校验之后批量导入.单从技术层面来说,导入操作通常情况下是一个比较耗时的操作,而且如果联系人达到几万.几十万级别,必须拆分成为子任务来执行.综上,可以使用线程池来解决问题.技术选型上,没有采用已有的 ThreadPoolExecutor 框架,而使用了自制的简易版线程池.该简易版的线程池,其实也是一个简易版的[生产者-消费者]模型,任务的加入就像是生产的过程,任务的处理就像是消费的过程.我们在这里不去讨论方案的合理性

Vue初体验——用Vue实现简易版TodoList

最近在学习Vue,断断续续看完了官方教程的基础部分,想练一下手熟悉一下基本用法,做了一个简易版的TodoList 效果: 代码: HTML: 1 <!DOCTYPE html> 2 <html> 3 4 <head> 5 <title>TodoList</title> 6 <meta charset="utf-8"> 7 <!-- 为了让 Bootstrap 开发的网站对移动设备友好,确保适当的绘制和触屏缩放

javaEE:day6-requset和response用法、表单参数的接受、文件手动上传(简易版)

通过<%=request.getContextPath() %> 可以将项目名写活,这样,即使项目名变了,仍可以运行. request代码每一次请求的容器.浏览器每次请求都是一个新的request对象.因此放在request里面的属性request.getAttribute()是空的,上一个request放的属性request.setAttribute() 是上一个的.与这次无关.但如果是转发的时候,那么request对象是共享的.这种情况下,两次的request是同一个.这种情况下,里面的属