手寫簡易Tomcat核心實現:深入理解Servlet容器原理

目錄

一、Tomcat概況

1. tomcat全局圖

2.項目結構概覽

二、實現步驟詳解

2.1 基礎工具包(com.qcby.util)

2.1.1 ResponseUtil:HTTP響應生成工具

2.1.2?SearchClassUtil:類掃描工具

2.1.3?WebServlet:自定義注解

2.2 Servlet核心實現包(com.qcby.webapps.servlet)

2.2.1?HttpServletRequest:請求對象

2.2.2?HttpServletResponse:響應對象

2.2.3?Servlet接口

2.2.4?GenericServlet抽象類

2.2.5?HttpServlet實現類

2.3 業務Servlet實現(com.qcby.webapps.myweb)

2.3.1 LoginServlet

2.3.2?ShowServlet

2.4?核心控制模塊(com.qcby)

2.4.1 ServletConfigMapping:Servlet配置中心

2.4.2 MyTomcat:服務器入口

2.5 運行流程全景圖

2.6 關鍵設計思想

2.6.1 控制反轉:

2.6.2 反射機制:

2.6.3 模板方法模式:

2.6.4 職責分離:

三、總結


????????本文將基于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 {@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.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 {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {// GET請求處理邏輯System.out.println("我是show");response.writeServlet(ResponseUtil.getResponseHeader200("show"));}@Overrideprotected 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();}}}}

核心流程

  1. 靜態初始化塊在類加載時執行

  2. 使用SearchClassUtil掃描所有類

  3. 通過反射獲取注解信息

  4. 實例化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中");}}}}

核心流程

  1. 創建ServerSocket監聽8081端口

  2. 循環接受客戶端連接

  3. 解析HTTP請求首行

  4. 根據路徑查找對應的Servlet

  5. 調用service方法處理請求

  6. 返回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,我們深入理解了:

  1. Servlet容器的啟動流程

  2. 請求-響應生命周期管理

  3. 注解驅動與反射的應用

  4. HTTP協議的基礎解析

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/71971.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71971.shtml
英文地址,請注明出處:http://en.pswp.cn/web/71971.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Java開發指南 | 第三十四篇】IDEA沒有Java Enterprise——解決方法

讀者可訂閱專欄&#xff1a;Java開發指南 |【CSDN秋說】 文章目錄 1、新建Java項目2、單擊項目名&#xff0c;并連續按兩次shift鍵3、在搜索欄搜索"添加框架支持"4、勾選Web應用程序5、最終界面6、添加Tomcat 1、新建Java項目 2、單擊項目名&#xff0c;并連續按兩次…

在MATLAB中實現PID控制仿真

在MATLAB中實現PID控制仿真可以通過代碼編程或Simulink圖形化建模兩種方式完成。以下是兩種方法的詳細操作步驟和示例&#xff1a; 方法1&#xff1a;使用MATLAB腳本編程&#xff08;基于控制系統工具箱&#xff09; 步驟1&#xff1a;定義被控對象的數學模型 假設被控對象是…

Conda常用命令匯總

Conda 是一個流行的包管理器和環境管理工具&#xff0c;廣泛應用于數據科學、機器學習等領域。它可以幫助我們管理 Python 包以及不同版本的環境&#xff0c;避免包沖突&#xff0c;提升項目的可復現性。以下是一些常用的 Conda 命令&#xff0c;涵蓋環境創建、管理、包安裝等常…

大數據實時分析:ClickHouse、Doris、TiDB 對比分析

1. 引言 在大數據分析領域,實時分析需求越來越重要。ClickHouse、Doris 和 TiDB 作為當前流行的實時分析數據庫,各自針對不同的應用場景和數據特性進行了優化。本文將對比它們的核心架構、適用場景以及查詢優化方法,幫助數據開發人員選擇合適的工具。 2. 核心架構對比 2.…

藍橋杯歷年真題題解

1.軌道炮&#xff08;數學模擬&#xff09; #include <iostream> #include <map> using namespace std; const int N1010; int x[N],y[N],v[N]; char d[N]; int main() {int n;int ans-100;cin>>n;for(int i1;i<n;i)cin>>x[i]>>y[i]>>v…

Pytorch的一小步,昇騰芯片的一大步

Pytorch的一小步&#xff0c;昇騰芯片的一大步 相信在AI圈的人多多少少都看到了最近的信息&#xff1a;PyTorch最新2.1版本宣布支持華為昇騰芯片&#xff01; 1、 發生了什么事兒&#xff1f; 在2023年10月4日PyTorch 2.1版本的發布博客上&#xff0c;PyTorch介紹的beta版本…

嵌入式硬件篇---手柄控制控制麥克納姆輪子

文章目錄 前言1. 變量定義2. 搖桿死區設置3. 模式檢查4. 搖桿數據處理4.1 右搖桿垂直值&#xff08;psx_buf[7]&#xff09;4.2 右搖桿水平值&#xff08;psx_buf[8]&#xff09;4.3 左搖桿水平值&#xff08;psx_buf[5]&#xff09;4.4 左搖桿垂直值&#xff08;psx_buf[6]&am…

阿里千問大模型(Qwen2.5-VL-7B-Instruct)部署

參考鏈接 知乎帖子 B站視頻 huggingface 鏡像網站&#xff08;不太全&#xff0c;比如 Qwen/Qwen2.5-VL-7B-Instruct就沒有&#xff09; huggingface 5種下載方式匯總 通過huggingface-cli下載模型 不一樣的部分是預訓練權重的下載和demo 首先安裝huggingface_hub pip insta…

Jenkins在Windows上的使用(二):自動拉取、打包、部署

&#xff08;一&#xff09;Jenkins全局配置 訪問部署好的Jenkins服務器網址localhost:8080&#xff0c;完成默認插件的安裝后&#xff0c;接下來將使用SSH登錄遠程主機以實現自動化部署。 1. 配置插件 選擇dashboard->Manage Jenkins->plugins 安裝下面兩個插件  …

群暉DS 223 Docker:開啟私有云

群暉DS 223 Docker&#xff1a;開啟私有云的無限可能 引言 在數據存儲與管理的不斷演進中&#xff0c;群暉 DS 223 憑借其出色的性能和豐富的功能&#xff0c;成為眾多用戶搭建私有云的熱門選擇。而當它與 Docker 技術相遇&#xff0c;猶如為數據管理的舞臺添上了絢麗多彩的燈…

git切換版本

git brach 查看本地 剛從git上下載下來 的話 可以通過 git checkout xxxx進行切換 可能一段時間沒有用 而服務器上新建了某些版本 那么需要用 git fetch origin 同步本地與git服務器的分支 然后 創建本地分支xxx 并從服務器拉取xxx git checkout -b xxx origin/xxx…

Three.js 進階(燈光陰影關系和設置、平行光、陰影相機)

本篇主要學習內容 : 燈光與陰影聚光燈點光源平行光陰影相機和陰影計算投射陰影接受陰影 點贊 關注 收藏 學會了 1.燈光與陰影 1、材質要滿足能夠對光有反應 2、設置渲染器開啟陰影計算 renderer.shadowMap.enabledtrue 3、設置光照投射陰影 directionalLight.castShadow …

【 <一> 煉丹初探:JavaWeb 的起源與基礎】之 Tomcat 的工作原理:從啟動到請求處理的流程

<前文回顧> 點擊此處查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、Tomcat…

【GPT入門】第11課 FunctionCall調用本地代碼入門

【GPT入門】第11課 FunctionCall調用代碼入門 1. 手撕FunctionCall2.代碼3.functionCall的結果 1. 手撕FunctionCall 為了了解&#xff0c;funcationCall底層&#xff0c;手寫一個functionCall多方法&#xff0c;并調用&#xff0c;體驗 思路&#xff1a; 任務&#xff1a;讓…

MySQL主從架構配合ShardingJdbc實現讀寫分離

文章目錄 目錄架構搭建讀寫分離pom.xmlfdy-live-user-provider 模塊application.ymlfdy-db-sharding.yamlShardingJdbcDatasourceAutoInitConnectionConfig.java 目錄 架構搭建 基于Docker去創建MySQL的主從架構 讀寫分離 pom.xml <dependency><groupId>mysql…

計網面試準備

正確理解網絡數據傳輸過程 同一路由器的不同接口屬于不同局域網&#xff0c;廣播只能在同一個局域網

NLP常見任務專題介紹(1)-關系抽取(Relation Extraction, RE)任務訓練模板

?? 關系抽取(Relation Extraction, RE)任務訓練示例 本示例展示如何訓練一個關系抽取模型,以識別兩個實體之間的關系。 1?? 任務描述 目標:從文本中提取兩個實體之間的語義關系,例如 “人物 - 組織”、“藥物 - 疾病”、“公司 - 創始人” 等。輸入:句子 + 標注的實…

【技術白皮書】內功心法 | 第二部分 | Telnet遠程登錄的工作原理

遠程登錄的工作原理 背景介紹遠程登錄遠程登錄的服務模式遠程登錄服務的實現基礎遠程登錄服務的運行模式Telnet服務為什么不被操作系統管理 Telnet協議的原理網絡虛終端&#xff08;NVT&#xff09;結束標示NVT的原理NVT屏蔽差異 背景介紹 絕大多數計算機都是運行多用戶操作系…

在 Spring Boot 中實現基于 TraceId 的日志鏈路追蹤

1 前言 1.1 什么是 TraceId? TraceId 是一個唯一的標識符,用于跟蹤分布式系統中的請求。每個請求從客戶端發起到服務端處理,再到可能的多個微服務調用,都會攜帶這個 TraceId,以便在整個請求鏈路中進行追蹤和調試。 1.2 日志鏈路追蹤的意義 日志鏈路追蹤可以幫助開發者…

游戲引擎學習第150天

回顧與當天計劃 我們在這里完全不使用任何庫&#xff0c;所以我們完全是引擎和庫免疫的, 正如大家所知道的&#xff0c;我們正在編寫自己的資源處理系統&#xff0c;準確來說&#xff0c;是一個資源加載系統。過去一周我們已經完成了很多工作&#xff0c;現在只剩下最后幾步&a…