基于Java实现简易Tomcat容器的核心代码解析(部分)

一、Servlet接口体系实现

1. ​Servlet接口定义

public interface Servlet {
    public void init();                        // 容器初始化时调用(生命周期起点)
    public void service(                       // 处理请求的核心入口
        HttpServletRequest request, 
        HttpServletResponse response
    );  
    public void destroy();                     // 容器关闭时调用(资源回收)
}

2. ​通用抽象类实现

public abstract class GenericServlet implements Servlet {
    public void init() {
        System.out.println("初始化servlet");    // 默认初始化逻辑(可被子类覆盖)
    }
    
    public void destroy() {
        System.out.println("销毁servlet");      // 默认销毁逻辑(可被子类覆盖)
    }
}

3. ​HTTP协议专属实现

public abstract class HttpServlet extends GenericServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response) {
        // 根据请求方法动态分发逻辑
        if (request.getMethod().equals("GET")) {
            doGet(request, response);          // 触发GET请求处理
        }
        else if (request.getMethod().equals("POST")) {
            doPost(request, response);         // 触发POST请求处理
        }
    }

    // 子类必须实现的抽象方法(框架级约束)
    protected abstract void doGet(HttpServletRequest request, HttpServletResponse response);
    protected abstract void doPost(HttpServletRequest request, HttpServletResponse response);
}

二、容器启动类(MyTomcat.java)

1. ​主入口与初始化

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

    public static void main(String[] args) throws IOException {
        ServletConfigMapping.init();          // 加载所有@WebServlet注解类
        ServerSocket serverSocket = new ServerSocket(8080); // 绑定8080端口
        
        while(true){                         // 永久监听循环
            Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
            InputStream inputStream = socket.getInputStream();

            // 读取请求原始字节流
            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("原始请求数据:\n" + Context); 

2. ​请求解析与路由

            if(!Context.isEmpty()){
                // 解析HTTP协议首行(示例:GET /index HTTP/1.1)
                String firstLine = Context.split("\\n")[0]; // 按换行符切割取首行
                String[] parts = firstLine.split("\\s");    // 按空格切割
                
                // 封装请求对象
                request.setMethod(parts[0]);  // 第一个元素为方法类型
                request.setPath(parts[1]);    // 第二个元素为请求路径
                System.out.println("解析结果: " + request.getMethod() + " " + request.getPath());
            }

            // 路由查找逻辑
            if(servletContext.containsKey(request.getPath())){
                HttpServlet servlet = servletContext.get(request.getPath());
                
                // 输出 Servlet 对象的映射信息
                System.out.println( request.getPath() + ": " + servlet);
                System.out.println("路由成功 -> " + servlet.getClass().getName());
                servlet.service(request, new HttpServletResponse()); // 调用Servlet入口
            }else{
                System.out.println("没有找到对应的Servlet");
            }
        }
    }
}

三、Servlet注册管理(ServletConfigMapping.java)

1. ​注解扫描与实例化

public class ServletConfigMapping {
    //1、扫描com.qcby.webapps下面的所有文件,并获取全路径名
    //2、根据全路径名获取类对象,在获取类对象上的@WebServlet注解
    //3、创建servlet对象,并注册到servletContext中

    // servlet 容器
    public  static Map servletContext = new HashMap<>();
    public static void init() {
        List classesPath = SearchClassUtil.searchClass();
        for (String s: classesPath) {
            try {
                getWebServletAnnotation(s);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //System.out.println("Servlet registered with path: " + path);
    }

    // 获取类上的@WebServlet注解
    private static void getWebServletAnnotation(String className) throws Exception {

            // 使用全类名加载类
            Class clazz = Class.forName(className);
            // 获取类上的@WebServlet注解
            WebServlet webServlet = clazz.getAnnotation(WebServlet.class);
            // 注册servlet
            servletContext.put(webServlet.urlMapping(), (HttpServlet) clazz.newInstance());

    }
}

2. ​自定义注解定义

@Retention(RetentionPolicy.RUNTIME) //表明注解仅用于源代码级别
@Target(ElementType.TYPE) //表明注解可以用于类上
public @interface WebServlet {
    String urlMapping() default "/"; //urlPatterns属性,默认值是"/"
}


四、请求/响应对象实现

1. ​请求对象封装

public class HttpServletRequest {
    private String path;                // 请求路径(如"/login")
    private String method;              // HTTP方法(GET/POST)

    // 工具方法:通过setter注入数据
    public void setPath(String path) { this.path = path; }
    public void setMethod(String method) { this.method = method; }
    
    // 供Servlet获取请求信息
    public String getPath() { return path; }
    public String getMethod() { return method; }
}

2. ​响应对象骨架

public class HttpServletResponse {
    // 预留扩展点(后续可添加状态码、响应头等功能)
}

五、工作流程示例

1. ​定义业务Servlet

@WebServlet(urlMapping = "/login")
public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("处理/api的GET请求");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("处理/api的POST请求");
    }
}

2. ​控制台输出示例

GET /login HTTP/1.1
/login: com.qcby.webapps.myweb.LoginServlet@610455d6
路由成功 -> com.qcby.webapps.myweb.LoginServlet
处理/login的GET请求

你可能感兴趣的:(tomcat,servlet,java)