目錄
一、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();}}}}
核心流程:
-
靜態初始化塊在類加載時執行
-
使用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協議的基礎解析