手写简易Tomcat核心实现:深入理解Servlet容器原理

目录

一、Tomcat概况

[1. tomcat全局图](#1. tomcat全局图)

2.项目结构概览

二、实现步骤详解

[2.1 基础工具包(com.qcby.util)](#2.1 基础工具包(com.qcby.util))

[2.1.1 ResponseUtil:HTTP响应生成工具](#2.1.1 ResponseUtil:HTTP响应生成工具)

[2.1.2 SearchClassUtil:类扫描工具](#2.1.2 SearchClassUtil:类扫描工具)

[2.1.3 WebServlet:自定义注解](#2.1.3 WebServlet:自定义注解)

[2.2 Servlet核心实现包(com.qcby.webapps.servlet)](#2.2 Servlet核心实现包(com.qcby.webapps.servlet))

[2.2.1 HttpServletRequest:请求对象](#2.2.1 HttpServletRequest:请求对象)

[2.2.2 HttpServletResponse:响应对象](#2.2.2 HttpServletResponse:响应对象)

[2.2.3 Servlet接口](#2.2.3 Servlet接口)

[2.2.4 GenericServlet抽象类](#2.2.4 GenericServlet抽象类)

[2.2.5 HttpServlet实现类](#2.2.5 HttpServlet实现类)

[2.3 业务Servlet实现(com.qcby.webapps.myweb)](#2.3 业务Servlet实现(com.qcby.webapps.myweb))

[2.3.1 LoginServlet](#2.3.1 LoginServlet)

[2.3.2 ShowServlet](#2.3.2 ShowServlet)

[2.4 核心控制模块(com.qcby)](#2.4 核心控制模块(com.qcby))

[2.4.1 ServletConfigMapping:Servlet配置中心](#2.4.1 ServletConfigMapping:Servlet配置中心)

[2.4.2 MyTomcat:服务器入口](#2.4.2 MyTomcat:服务器入口)

[2.5 运行流程全景图](#2.5 运行流程全景图)

[2.6 关键设计思想](#2.6 关键设计思想)

[2.6.1 控制反转:](#2.6.1 控制反转:)

[2.6.2 反射机制:](#2.6.2 反射机制:)

[2.6.3 模板方法模式:](#2.6.3 模板方法模式:)

[2.6.4 职责分离:](#2.6.4 职责分离:)

三、总结


本文将基于Java实现一个极简版Tomcat,通过代码逐层剖析Servlet容器的工作原理。读者将掌握HTTP协议解析、注解驱动开发、反射机制、类加载等核心技术。

一、Tomcat概况

1. tomcat全局图

2.项目结构概览

src
├── com.qcby
│   ├── MyTomcat.java          // 服务启动类
│   └── ServletConfigMapping.java // Servlet配置映射
├── com.qcby.util
│   ├── ResponseUtil.java      // HTTP响应工具
│   ├── SearchClassUtil.java   // 类扫描工具
│   └── WebServlet.java        // 自定义注解
└── com.qcby.webapps
    ├── myweb                  // 示例Servlet
    └── servlet                // Servlet核心实现

二、实现步骤详解

2.1 基础工具包(com.qcby.util)

2.1.1 ResponseUtil:HTTP响应生成工具

java 复制代码
package com.qcby.util;

public class ResponseUtil {
    // 200响应头模板
    public static final String responseHeader200 = 
        "HTTP/1.1 200 \r\nContent-Type:text/html; charset=utf-8 \r\n\r\n";
    
    // 生成404响应
    public static String getResponseHeader404(){
        return "HTTP/1.1 404 \r\nContent-Type:text/html; charset=utf-8 \r\n\r\n404";
    }
    
    // 生成带内容的200响应
    public static String getResponseHeader200(String context){
        return responseHeader200 + context;
    }
}

核心作用

  • 标准化HTTP响应格式

  • 提供快速构建响应的静态方法

  • 支持200/404状态码生成

2.1.2 SearchClassUtil:类扫描工具

java 复制代码
package com.qcby.util;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**'
 * 扫描com.qcby.webapps目录下面的文件,获取每一个Java文件的全类名
 */
public class SearchClassUtil {
    public static List<String> classPaths = new ArrayList<String>();

    public static List<String> searchClass(){
        //需要扫描的包名
        String basePack = "com.qcby.webapps.myweb";
        //将获取到的包名转换为路径
        String classPath = SearchClassUtil.class.getResource("/").getPath();
        basePack =  basePack.replace(".", File.separator);
        String searchPath = classPath + basePack;
        doPath(new File(searchPath),classPath);
        //这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
        return classPaths;
    }

    /**
     * 该方法会得到所有的类,将类的绝对路径写入到classPaths中
     * @param file
     */
    private static void doPath(File file,String classpath) {
        if (file.isDirectory()) {//文件夹
            //文件夹我们就递归
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1,classpath);
            }
        } else {//标准文件
            //标准文件我们就判断是否是class文件
            if (file.getName().endsWith(".class")) {
                String path = file.getPath().replace(classpath.replace("/","\\").
                                replaceFirst("\\\\",""),"").replace("\\",".").
                        replace(".class","");
                //如果是class文件我们就放入我们的集合中。
                classPaths.add(path);
            }
        }
    }

    public static void main(String[] args) {
        List<String> classes = SearchClassUtil.searchClass();
        for (String s: classes) {
            System.out.println(s);
        }
    }
}

核心作用

  • 递归扫描指定包路径

  • 转换.class文件为全限定类名

  • 支持动态类加载的基础

2.1.3 WebServlet:自定义注解

java 复制代码
package com.qcby.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)//源文件阶段保留
@Target(ElementType.TYPE)//表明@WebSerlet注解是写在类上
public @interface WebServlet {

    //String path=null;
    String urlMapping() default "" ;
}

核心作用

  • 替代XML配置的注解式声明

  • 建立Servlet与URL的映射关系

  • 运行时(RUNTIME)保留策略支持反射读取可读取注解信息

2.2 Servlet核心实现包(com.qcby.webapps.servlet)

2.2.1 HttpServletRequest:请求对象

java 复制代码
package com.qcby.webapps.servlet.req;

public class HttpServletRequest {
    private String path;
    private String method;


    public String getPath(){return path;
    }

    public void setPath(String path){this.path=path;}
    public String getMethod(){return method;}
    public void setMethod(String method){this.method=method;}
}
  • 职责分离:请求封装客户端数据,响应处理输出流

2.2.2 HttpServletResponse:响应对象

java 复制代码
package com.qcby.webapps.servlet.req;

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

public class HttpServletResponse {
    private OutputStream outputStream;
    public HttpServletResponse(OutputStream outputStream ){
        this.outputStream=outputStream;
    }
    public void writeServlet(String context) throws IOException{
        outputStream.write(context.getBytes());
    }

}

2.2.3 Servlet接口

java 复制代码
package com.qcby.webapps.servlet;

import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;

import java.io.IOException;

public interface Servlet<response> {
    public void init();
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
    public void destroy();

}

2.2.4 GenericServlet抽象类

java 复制代码
package com.qcby.webapps.servlet;


public abstract  class GenericServlet implements  Servlet {
    public void init(){
        System.out.println("------初始化servlet------");
    }
    public void destroy(){
        System.out.println("------实现servlet对象的销毁------");
    }

}

2.2.5 HttpServlet实现类

java 复制代码
package com.qcby.webapps.servlet;

import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;

import java.io.IOException;

public  abstract  class HttpServlet extends GenericServlet{

    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {

        if(request.getMethod().equals("GET")){
            doGet(request,response);
        }
        else{
            doPost(request,response);
        }
    }
    protected  abstract  void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException;


    protected  abstract  void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException;

}
  • 设计模式:模板方法模式处理HTTP方法分发

  • 继承体系:HttpServlet -> GenericServlet -> Servlet

核心作用

  • 构建完整的Servlet生命周期

  • 实现HTTP方法的分发机制

  • 提供请求响应的标准接口

2.3 业务Servlet实现(com.qcby.webapps.myweb)

2.3.1 LoginServlet

java 复制代码
package com.qcby.webapps.myweb;

import com.qcby.util.ResponseUtil;
import com.qcby.util.WebServlet;
import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;

import java.io.IOException;
@WebServlet(urlMapping = "/login")
public class LoginServlet  extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println("我是login的doGet方法");
        response.writeServlet(ResponseUtil.getResponseHeader200("hello"));
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

    }
}

2.3.2 ShowServlet

java 复制代码
package com.qcby.webapps.myweb;

import com.qcby.util.ResponseUtil;
import com.qcby.util.WebServlet;

import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;

import java.io.IOException;


@WebServlet(urlMapping="/show")
public class ShowServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // GET请求处理逻辑
        System.out.println("我是show");
        response.writeServlet(ResponseUtil.getResponseHeader200("show"));
    }


    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        // POST请求处理逻辑
    }
}

核心作用

  • 演示具体业务实现

  • 展示注解驱动的路由配置

  • 体现模板方法模式的应用

2.4 核心控制模块(com.qcby)

2.4.1 ServletConfigMapping:Servlet配置中心

java 复制代码
package com.qcby;

import com.qcby.util.SearchClassUtil;
import com.qcby.util.WebServlet;
import com.qcby.webapps.servlet.HttpServlet;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServletConfigMapping {

    public static Map<String, HttpServlet> servletMap =new HashMap<>();
    static{
        List<String> classNames = SearchClassUtil.searchClass();
        for (String path:classNames){
            try{
                Class clazz =Class.forName(path);
                WebServlet webServlet =(WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
                HttpServlet servlet =(HttpServlet) clazz.newInstance();
                servletMap.put(webServlet.urlMapping(), servlet);
                System.out.println(servletMap);

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

}

核心流程

  1. 静态初始化块在类加载时执行

  2. 使用SearchClassUtil扫描所有类

  3. 通过反射获取注解信息

  4. 实例化Servlet并建立URL映射

2.4.2 MyTomcat:服务器入口

java 复制代码
package com.qcby;

import com.qcby.webapps.servlet.HttpServlet;
import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import static com.qcby.ServletConfigMapping.servletMap;

public class MyTomcat {
    static HttpServletRequest request =new HttpServletRequest();

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket =new ServerSocket(8081);
        while (true){
            Socket socket =serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream =socket.getOutputStream();
            HttpServletResponse response= new HttpServletResponse(outputStream);

            int count = 0;
            while (count == 0){
                count = inputStream.available();
        }

            byte[] bytes = new byte[count];
            inputStream.read(bytes);
            String Context = new String(bytes);
            System.out.println(Context);

            //解析数据
            if(Context.equals("")){
                System.out.println("你输入了一个空请求");
            }else {
                String firstLine = Context.split("\\n")[0]; //根据换行来获取第一行数据
                request.setPath(firstLine.split("\\s")[1]);
                request.setMethod(firstLine.split("\\s")[0]);
            }



            if(servletMap.containsKey(request.getPath())){
                System.out.println("存在于HashMap中");
                HttpServlet servlet=ServletConfigMapping.servletMap.get(request.getPath());
                servlet.service(request,response);
            } else {
                System.out.println("不存在于HashMap中");
            }



        }

}}

核心流程

  1. 创建ServerSocket监听8081端口

  2. 循环接受客户端连接

  3. 解析HTTP请求首行

  4. 根据路径查找对应的Servlet

  5. 调用service方法处理请求

  6. 返回404响应处理异常路径

2.5 运行流程全景图

java 复制代码
启动流程:
1. MyTomcat.main()启动
2. ServletConfigMapping静态块初始化
   → SearchClassUtil扫描类
   → 反射创建Servlet实例
   → 建立URL映射表

请求处理流程:
客户端请求 → ServerSocket接收 → 解析请求路径 → 
查找Servlet映射 → 调用service() → 执行doGet/doPost → 
生成响应 → 返回客户端

2.6 关键设计思想

2.6.1 控制反转

  • Servlet实例由容器创建
  • 开发者通过注解声明路由

2.6.2 反射机制

  • 动态加载类文件
  • 读取注解配置信息

2.6.3 模板方法模式

  • HttpServlet定义处理骨架
  • 子类实现具体业务逻辑

2.6.4 职责分离

  • Request/Response对象封装协议细节
  • Util类处理通用逻辑

三、总结

通过实现这个简易Tomcat,我们深入理解了:

  1. Servlet容器的启动流程

  2. 请求-响应生命周期管理

  3. 注解驱动与反射的应用

  4. HTTP协议的基础解析

相关推荐
夏天的味道٥3 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
冰糖码奇朵5 小时前
大数据表高效导入导出解决方案,mysql数据库LOAD DATA命令和INTO OUTFILE命令详解
java·数据库·sql·mysql
好教员好5 小时前
【Spring】整合【SpringMVC】
java·spring
浪九天6 小时前
Java直通车系列13【Spring MVC】(Spring MVC常用注解)
java·后端·spring
堕落年代6 小时前
Maven匹配机制和仓库库设置
java·maven
功德+n7 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven
香精煎鱼香翅捞饭7 小时前
java通用自研接口限流组件
java·开发语言
ChinaRainbowSea7 小时前
Linux: Centos7 Cannot find a valid baseurl for repo: base/7/x86_64 解决方案
java·linux·运维·服务器·docker·架构
囧囧 O_o8 小时前
Java 实现 Oracle 的 MONTHS_BETWEEN 函数
java·oracle
去看日出8 小时前
RabbitMQ消息队列中间件安装部署教程(Windows)-2025最新版详细图文教程(附所需安装包)
java·windows·中间件·消息队列·rabbitmq