当前位置: 首页 > article >正文

DIY Tomcat:手写一个简易Servlet容器

在Java Web开发领域,Tomcat堪称经典,它作为Servlet容器,承载着无数Web应用的运行。今天,我将带大家一同探索如何手写一个简易的Tomcat,深入理解其底层原理。

一、背景知识

在开始之前,我们需要对几个关键概念有所了解:

  1. Servlet :是一种运行在服务器端的 Java 接口,用于响应客户端请求并生成动态内容。

  2. Servlet 容器 :负责管理 Servlet 的生命周期,包括加载、实例化、调用和销毁等操作。

  3. Socket 编程 :用于实现网络通信,Tomcat 通过监听 Socket 接收客户端请求。

二、核心代码解析

1. MyTomcat.java

这是整个简易 Tomcat 的核心入口。我们创建了一个 ServerSocket 对象,监听 8080 端口,等待客户端的连接。

ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket socket = serverSocket.accept();
    // 处理客户端请求的逻辑
}

当有客户端连接时,通过 Socket 获取输入流,读取请求信息,并解析出请求方法和路径。

 InputStream inputStream= socket.getInputStream();
            HttpServletResponse response=new HttpServletResponse(socket);
            HttpServletRequest request=new HttpServletRequest();
            int count=0;
            while(count==0){
                count=inputStream.available();
            }
            byte[] bytes=new byte[count];
            inputStream.read(bytes);
            String context=new String(bytes);
// 解析请求方法和路径
String Context = new String(bytes);
String firstLine = Context.split("\\n")[0];
String method = firstLine.split("\\s")[0];
String path = firstLine.split("\\s")[1];
request.setPath(path);
request.setMethod(method);

根据解析出的路径,在 Servlet 容器中查找对应的 Servlet 对象,并调用其 service 方法。

if(ServletConfigMapping.servletMap.get(request.getPath())!=null)
{
    System.out.println("------------------");
    HttpServlet ClassServlet=ServletConfigMapping.servletMap.get(request.getPath());
    ClassServlet.service(request,  response);
}

2. ServletConfigMapping.java

这个类负责初始化 Servlet 容器,扫描指定包下的所有类,获取带有 @WebServlet 注解的类,并将其路径和对象存入 HashMap 中。

public static void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    List<String> classesPath= SearchClassUtil.searchClass();
    for(String path:classesPath){
        getMessage(path);
    }
}

public static void getMessage(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    Class clazz=Class.forName(classPath);
    WebServlet webServlet=(WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
    HttpServlet servlet=(HttpServlet) clazz.newInstance();
    servletMap.put(webServlet.urlMapping(),servlet);
}

3. LoginServlet.java 和 ShowServlet.java

这两个类是具体的 Servlet 实现,继承自 HttpServlet,并重写 doGet 和 doPost 方法,用于处理不同的 HTTP 请求。

@WebServlet(urlMapping = "/login")
public class LoginServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        // 处理 GET 请求的逻辑
    }

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

4. SearchClassUtil.java

该工具类用于扫描指定包下的所有类文件,获取其全类名,为 Servlet 容器的初始化提供类路径信息。

public static List<String> searchClass() {
    String basePack = "com.qcby.webapps";
    String classPath = SearchClassUtil.class.getResource("/").getPath();
    try {
        classPath = URLDecoder.decode(classPath, StandardCharsets.UTF_8.name());
    } catch (Exception e) {
        e.printStackTrace();
    }
    basePack = basePack.replace(".", File.separator);
    String searchPath = classPath + basePack;
    doPath(new File(searchPath), classPath);
    return classPaths;
}

5. WebServlet.java

这是一个自定义注解,用于标注 Servlet 类,并指定其映射的 URL 路径。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServlet {
    String urlMapping() default "";
}

6. GenericServlet.java、HttpServlet.java 和 Servlet.java

这些类和接口构成了 Servlet 的基本体系结构。Servlet 接口定义了 Servlet 的生命周期方法,GenericServlet 提供了通用的实现,HttpServlet 则针对 HTTP 请求进行了特殊处理,将请求分发到 doGet 或 doPost 方法。

public interface Servlet {
    void init();
    void destroy();
    void service(HttpServletRequest request, HttpServletResponse response);
}

public abstract class GenericServlet implements Servlet{
    @Override
    public void init() {}
    @Override
    public void destroy() {}
}

public abstract class HttpServlet extends GenericServlet {
    public void service(HttpServletRequest request, HttpServletResponse response){
        if(request.getMethod().equals("GET"))
        {
            doGet(request,response);
        }
        else if(request.getMethod().equals("POST"))
        {
            doPost(request,response);
        }
    }
    public abstract void doGet(HttpServletRequest request,HttpServletResponse response);
    public abstract void doPost(HttpServletRequest request,HttpServletResponse response);
}

7. HttpServletRequest.java 和 HttpServletResponse.java

这两个类用于封装请求和响应信息,方便在 Servlet 中进行操作。

public class HttpServletRequest {
    private String method;
    private String path;
    // getter 和 setter 方法
}

public class HttpServletResponse {
}

三、运行效果展示

将上述代码整合后,启动 MyTomcat,它便会开始监听 8080 端口。当我们在浏览器中访问对应的路径,如 http://localhost:8080/loginhttp://localhost:8080/show 时,Tomcat 会根据请求路径找到对应的 Servlet,并调用其 doGet 或 doPost 方法来处理请求,最终返回相应的响应。

四、总结与展望

通过手写这个简易的 Tomcat,我们深入理解了 Servlet 容器的基本工作原理,包括 Socket 编程、请求解析、Servlet 的加载和调用等关键环节。虽然这个实现功能简单,但它为我们进一步学习和研究 Tomcat 的源码提供了宝贵的实践经验。

在未来的学习中,我们可以继续完善这个简易 Tomcat,添加更多的功能,如支持静态资源的访问、会话管理、过滤器和监听器等,逐步使其功能更加丰富和完善,向真正的 Tomcat 靠拢。

希望这次的分享能为大家打开 Java Web 开发底层世界的大门,让我们一起在技术的海洋中不断探索和前行!


http://www.kler.cn/a/576853.html

相关文章:

  • HTTPS安全通信协议原理
  • 什么是 Java 的 Timer?
  • opentitan riscv
  • 关于父组件向子组件传值的形式(类型一)
  • 【商城实战(13)】购物车价格与数量的奥秘
  • 【leetcode hot 100 142】环形链表Ⅱ
  • webflux响应式编程
  • Windows、macOS和Linux系统的统计文件夹下的文件数量的方法
  • 笔记:代码随想录算法训练营day38: LeetCode322. 零钱兑换、279.完全平方数、139.单词拆分;多重背包
  • 数学建模:MATLAB强化学习
  • MacBook上API调⽤⼯具推荐
  • 线性代数笔记28--奇异值分解(SVD)
  • 【从零开始学习计算机科学】数字逻辑(五) Verilog HDL语言
  • Lab17_ Blind SQL injection with out-of-band data exfiltration
  • MTK Android12 桌面上显示文件管理器图标
  • 深入剖析 ConcurrentHashMap:高并发场景下的高效哈希表
  • 查看k8s集群的资源使用情况
  • Azure云生态系统详解:核心服务、混合架构与云原生概念
  • 一文了解基于AUTOSAR的ECU传感器信号处理全流程
  • [PWNME 2025] PWN 复现