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

基于自定义Tomcat实现资源访问的完整指南

文章目录

  • 前言:从零构建轻量级Web容器
  • 一、环境准备与项目搭建
    • 1. 创建Maven项目
    • 2. 项目结构
  • 二、核心组件实现解析
    • 1.HTTP协议处理层
    • 2. Servlet容器架构
  • 三、注解驱动配置
    • 1. 自定义WebServlet注解
    • 2. 组件扫描与注册
  • 四、服务器核心逻辑
    • 1. BIO网络模型实现
    • 2. HTTP请求解析
    • 3. Servlet路由分发
    • 4. 响应处理封装
  • 五、开发测试Servlet
    • 1. 创建业务Servlet
    • 2.运行与测试
  • 六、核心原理深度解析
  • 总结


前言:从零构建轻量级Web容器

本文将带您实现一个精简版Tomcat的核心功能,重点讲解如何通过Java原生Socket处理HTTP请求、注解驱动配置Servlet、以及实现静态资源访问。通过这个项目,您将深入理解以下核心知识点:

  • HTTP协议报文解析原理
  • Servlet容器的工作机制
  • 反射与注解实现组件扫描
  • 阻塞式I/O模型的应用
  • Maven项目结构管理

一、环境准备与项目搭建

1. 创建Maven项目

<!-- pom.xml核心依赖 -->
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>

2. 项目结构

在这里插入图片描述

二、核心组件实现解析

1.HTTP协议处理层

  • 报文解析:通过解析HTTP请求首行(GET /path HTTP/1.1),提取请求方法和路径。

  • 请求/响应对象:封装HttpServletRequest(存储路径、方法)和HttpServletResponse(操作输出流)。

package com.qcby.webapps.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;
    }
}


//响应对象
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. Servlet容器架构

  • 接口分层:

    • Servlet接口定义生命周期方法(init/service/destroy)。

    • GenericServlet提供默认实现,HttpServlet实现HTTP方法分发(doGet/doPost)。

  • 路由映射:通过路径匹配调用对应的Servlet实例。

package com.qcby.webapps.servlet;

import com.qcby.webapps.servlet.req.HttpServletRequest;
import com.qcby.webapps.servlet.req.HttpServletResponse;
import java.io.IOException;
// Servlet接口定义
public interface Servlet<response> {
    public void init();
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException;
    public void destroy();

}
// 抽象类实现通用逻辑
public abstract  class GenericServlet implements  Servlet {
    public void init(){
        System.out.println("------初始化servlet------");
    }
    public void destroy(){
        System.out.println("------实现servlet对象的销毁------");
    }
}
// HTTP方法分发器
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;

}

三、注解驱动配置

1. 自定义WebServlet注解

  • 自定义注解:@WebServlet(urlMapping)标记Servlet类并绑定URL路径。
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 "" ;
}

2. 组件扫描与注册

  • 组件扫描:利用反射扫描类路径,自动注册带注解的Servlet到映射表。
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. BIO网络模型实现

ServerSocket serverSocket = new ServerSocket(8585);
Socket socket = serverSocket.accept(); // 阻塞点
  • 阻塞监听:accept()方法阻塞线程直到新连接到达

2. HTTP请求解析

while(true){
 int count = 0;
            while (count == 0){
                count = inputStream.available();
        }
// 读取请求数据
byte[] bytes = new byte[count];
inputStream.read(bytes);
String Context = new String(bytes);

// 解析首行
String firstLine = Context.split("\\n")[0]; 
request.setPath(firstLine.split("\\s")[1]);
request.setMethod(firstLine.split("\\s")[0]);

  • 输入流处理:通过inputStream.available()获取数据长度(存在风险)
  • 协议解析:按换行符拆分请求头,提取请求方法和路径

3. Servlet路由分发

if(servletMap.containsKey(request.getPath())){
    HttpServlet servlet = servletMap.get(request.getPath());
    servlet.service(request,response); // 动态请求处理
} else {
    // 静态资源处理(待实现)
}
  • 映射机制:通过预加载的servletMap实现路径-Servlet绑定
  • 责任链模式:Servlet接口统一处理入口

4. 响应处理封装

public class HttpServletResponse {
    private OutputStream outputStream;  
    public void writeServlet(String context) throws IOException {
        outputStream.write(context.getBytes());
    }
}
  • 输出流封装:直接操作Socket的输出流

五、开发测试Servlet

1. 创建业务Servlet

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.运行与测试

在这里插入图片描述

六、核心原理深度解析

在这里插入图片描述

  • 关键技术点
  • 阻塞I/O模型:使用ServerSocket.accept()阻塞等待连接
  • 请求解析:通过拆解HTTP报文首行获取关键信息
  • 注解驱动:利用反射机制实现组件自动注册
  • 资源隔离:静态资源与动态请求分离处理
  • 响应封装:规范化HTTP响应头构造

总结

这为我们理解主流Web容器(如Tomcat、Jetty)的工作原理提供了坚实基础。后续可以在此基础上扩展会话管理、JSP支持等高级特性,逐步构建功能完备的Java Web容器。


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

相关文章:

  • 探索React:构建现代前端应用的强大框架
  • 1-1 驱动开发HelloWorld
  • 代码托管平台对比分析:Gitee与GitLab
  • SpringBoot-模拟SSE对话交互
  • 如何在PHP中实现有效的日志记录
  • git在cmd的操作
  • 使用DeepSeek + Python开发AI思维导图应用,非常强!
  • winform基于antdui中table控件的使用
  • 蓝桥杯备考:bfs之马的遍历
  • Android AudioFlinger(一)——初识AndroidAudio Flinger
  • Vite 6 升级指南:CJS 和 ESM 的爱恨情仇
  • 单例设计模式---懒汉式--线程安全和不安全、枚举类
  • 用AI学编程4——swift学习1
  • Mac软件卸载后启动台图标还在
  • 基于深度学习的恶意软件检测系统:设计与实现
  • springmvc_view介绍
  • 4、STL的deque使用方法
  • SpringBoot知识点及其源码解析(3)
  • 华为eNSP:实验 OSPF单区域
  • 4.归一化技术:深度网络中的关键优化手段——大模型开发深度学习理论基础