目录
2.2 Servlet核心实现包(com.qcby.webapps.servlet)
2.2.2 HttpServletResponse:响应对象
2.3 业务Servlet实现(com.qcby.webapps.myweb)
2.4.1 ServletConfigMapping:Servlet配置中心
本文将基于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响应生成工具
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:类扫描工具
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:自定义注解
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:请求对象
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:响应对象
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接口
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抽象类
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实现类
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
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
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配置中心
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();
}
}
}
}
核心流程:
静态初始化块在类加载时执行
使用SearchClassUtil扫描所有类
通过反射获取注解信息
实例化Servlet并建立URL映射
2.4.2 MyTomcat:服务器入口
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中");
}
}
}}
核心流程:
创建ServerSocket监听8081端口
循环接受客户端连接
解析HTTP请求首行
根据路径查找对应的Servlet
调用service方法处理请求
返回404响应处理异常路径
2.5 运行流程全景图
启动流程:
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,我们深入理解了:
Servlet容器的启动流程
请求-响应生命周期管理
注解驱动与反射的应用
HTTP协议的基础解析