文章目录
- 前言:从零构建轻量级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 {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {System.out.println("我是login的doGet方法");response.writeServlet(ResponseUtil.getResponseHeader200("hello"));}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {}
}
2.运行与测试
六、核心原理深度解析
- 关键技术点
- 阻塞I/O模型:使用ServerSocket.accept()阻塞等待连接
- 请求解析:通过拆解HTTP报文首行获取关键信息
- 注解驱动:利用反射机制实现组件自动注册
- 资源隔离:静态资源与动态请求分离处理
- 响应封装:规范化HTTP响应头构造
总结
这为我们理解主流Web容器(如Tomcat、Jetty)的工作原理提供了坚实基础。后续可以在此基础上扩展会话管理、JSP支持等高级特性,逐步构建功能完备的Java Web容器。