如何仿寫簡易tomcat 實現思路+代碼詳細講解

仿寫之前,我們要搞清楚都要用到哪些技術

  1. 自定義注解,比如Tomcat使用的是@Servlet,我們可以定義一個自己的@MyServlet
  2. 構造請求體和返回體,比如tomcat使用HttpRequest,我們可以自己定義myHttpRequest
  3. java去遍歷一個指定目錄,然后獲取到.java文件,再獲取到帶有@MyServlet注解的類
  4. 然后將這個注解里的path和這個類本身映射成map
  5. 通過反射去調用該類的方法(doGet、doPost)
  6. 還需要用到socket來監聽消息,并且對監聽到的消息進行處理

第一步:自定義注解

@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyServlet {String path() default "";
}

第二步:定義HttpRequest以及HttpResponse、

public class MyHttpRequest {//定義一個map,用來存放請求體中的參數,key是參數名稱,value是參數值public Map<String,String> map = new HashMap<>();public String getParameter(String key){return map.get(key);}
}
public class MyHttpResponse {public OutputStream outputStream;public static final String responsebody = "HTTP/1.1 200+\r\n" + "Content-Type:text/html+\r\n"+ "\r\n";public MyHttpResponse(OutputStream outputStream) {this.outputStream = outputStream;}
}

第三步:遍歷整個目錄,把Java文件放入list中

    private static void func(File file){File[] files = file.listFiles();String s;for (File file1 : files) {if (file1.isDirectory()){func(file1);}if (file1.isFile()){//取src之后的名字s = file1.toString().split("src")[1];//去掉src后邊的第一個\,得到全類名s = s.substring(1);//判斷是不是以.java結尾的文件if (s.length() >=5 && s.substring(s.length() - 5).equals(".java")){//把全類名中的\替換成.s = s.replace('\\','.');//去掉后綴名.javas = s.substring(0,s.length()-5);//把類名加入到list中javaclasses.add(s);}}}}

第四步:找出帶有Servlet注解的Java文件,并把注解中的path,類對象放入到map中

    public static void getServlet() throws ClassNotFoundException {for (int i = 0; i < javaclasses.size(); i++) {String path = javaclasses.get(i);Class<?> cl = Class.forName(path);if (cl.isAnnotationPresent(MyServlet.class)){servletMap.put(cl.getAnnotation(MyServlet.class).path(),cl);}}}

第五步:創建socket連接

InetAddress localHost = InetAddress.getLocalHost();System.out.println("localhost" + localHost);ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);System.out.println("等待建立連接");Socket server = serverSocket.accept();System.out.println("連接已建立");

第六步:定義線程接收報文

        HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);Thread accept = new Thread(httpAcceptThread);accept.start();accept.join();

HttpAcceptThread類內容如下:

class HttpAcceptThread implements Runnable{private Socket socket;ArrayList<String> strings = new ArrayList<>();public HttpAcceptThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {System.out.println("開始接收http");try {BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));String s;while ((s = reader.readLine()).length() != 0){try {strings.add(s);System.out.println(s);} catch (Exception e){System.out.println("接收Http進程結束");break;}}System.out.println("接收http進程結束");} catch (IOException e) {e.printStackTrace();}}
}

第七步:處理httprequest,也就是通過反射去調用doGet和doPost方法

這一步有些復雜,尤其是對url切割時,但我給每一步都加了注釋,方便理解

? ? ? ? ? ? ?GET /address1?a=111&b=222

   private static void requestHttp(Socket socket,String http) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {//GET /address1?a=111&b=222(拿獲取到的這個url舉例)//先通過空格判斷是GET還是POSTString requestStyle = http.split(" ")[0];if (requestStyle.equals("GET")){//如果是GET,取空格后面部分,即/address1?a=111&b=222String httpPathAndParameter = http.split(" ")[1];//定義httpPathString httpPath;//創建httpRequest對象MyHttpRequest myHttpRequest = new MyHttpRequest();//通過索引位置判斷url里邊有沒有帶?if (httpPathAndParameter.indexOf("?") != -1){//如果有,由于有個/,因此我們要先拿到address1?a=111&b=222這部分httpPath = httpPathAndParameter.substring(1);//獲取問號前面部分,即address1,\\作為轉義字符使用httpPath = httpPath.split("\\?")[0];System.out.println(httpPath);//獲取問號后面部分的所有參數String parameterString = httpPathAndParameter.split("\\?")[1];//使用&分開String[] parameters = parameterString.split("&");for (int i = 0; i < parameters.length; i++) {//把參數及其值仿佛request的map中myHttpRequest.map.put(parameters[i].split("=")[0],parameters[i].split("=")[1]);}} else {//如果不存在?,也就說明不存在參數,我們只需要獲取httpPathhttpPath = httpPathAndParameter.substring(1);System.out.println(httpPath);}//創建HttpResponse對象OutputStream outputStream = socket.getOutputStream();MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);//反射調用doGetClass servletClass = servletMap.get(httpPath);Method doGet = servletClass.getMethod("doGet", MyHttpRequest.class, MyHttpResponse.class);doGet.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);} else {//如果不是Get請求,也按照同樣的步驟,先取出/address1String httpPath = http.split(" ")[1];//去掉/,只留下address1httpPath = httpPath.substring(1);System.out.println(httpPath);MyHttpRequest myHttpRequest = new MyHttpRequest();OutputStream outputStream = socket.getOutputStream();MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);//根據httpPath取出類信息Class servletClass = servletMap.get(httpPath);//獲取doPost方法Method doPost = servletClass.getMethod("doPost", MyHttpRequest.class, MyHttpResponse.class);//調用doPost方法doPost.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);}}

最后一步:把上面這些方法整合起來,在主方法中調用,同時定義好全局變量

public class MyTomcat {//用于存放Java類的全類名public static ArrayList<String> javaclasses = new ArrayList<>();//用于存放Servlet的類對象,其中key是Servlet的url,value是servlet的類對象public static HashMap<String,Class> servletMap = new HashMap<>();public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {String inputPath = "D:\\JavaProject\\practice\\src\\tomcat";File file = new File(inputPath);//獲取.java后綴文件,并獲取全類名func(file);System.out.println(javaclasses);//獲取帶有servlet注解的類對象,并放到map中。getServlet();System.out.println(servletMap);InetAddress localHost = InetAddress.getLocalHost();System.out.println("localhost" + localHost);ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);System.out.println("等待建立連接");Socket server = serverSocket.accept();System.out.println("連接已建立");//定義線程接收http報文HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);Thread accept = new Thread(httpAcceptThread);accept.start();accept.join();//處理請求requestHttp(server,httpAcceptThread.strings.get(0));}

然后就可以進行測試了,在測試類上方加上我們已經定義好的@MyServlet注解

@MyServlet(path = "address1")
public class Servlet1 {public void doGet(MyHttpRequest request, MyHttpResponse response) throws IOException {System.out.println("address1 GET響應:");System.out.println("a=" + request.getParameter("a"));System.out.println("\n響應的http如下:");String resp = MyHttpResponse.responsebody + "<!DOCTYPE html>\n" +"<html>\n" +"<head>\n" +"    <meta charset=\"utf-8\" />\n" +"</head>\n" +"<body>\n" +" \n" +"    <form name=\"my_form\" method=\"POST\">\n" +"        <input type=\"button\" value=\"按下\" onclick=\"alert('你按下了按鈕')\">\n" +"    </form>\n" +" \n" +"</body>\n" +"</html>";System.out.println(resp);response.outputStream.write(resp.getBytes());response.outputStream.flush();response.outputStream.close();}public void doPost(MyHttpRequest request, MyHttpResponse response) throws IOException {System.out.println("\n響應的http如下:");String resp = MyHttpResponse.responsebody +"{\"sorry\":\"we only respond to method GET now\"},\r\n" +"";System.out.println(resp);response.outputStream.write(resp.getBytes());response.outputStream.flush();response.outputStream.close();}
}

然后啟動項目

?可以看到本機ip地址,然后通過瀏覽器地址欄訪問

?這樣就實現了一個簡單的tomcat

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

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

相關文章

Structs新增接口 報錯404,找不到資源

起因&#xff1a;最近在一個古老框架structs上開發新功能&#xff0c;由于之前沒接觸過&#xff0c;故此記錄 新增接口&#xff0c; 接口類&#xff1a; Path("/A") Produces({ MediaType.APPLICATION_JSON }) public interface Money {POSTPath("/B")Resu…

數據結構——鏈表詳解

鏈表 文章目錄 鏈表前言認識鏈表單鏈表結構圖帶頭單循環鏈表結構圖雙向循環鏈表結構圖帶頭雙向循環鏈表結構圖 鏈表特點 鏈表實現(帶頭雙向循環鏈表實現)鏈表結構體(1) 新建頭節點(2) 建立新節點(3)尾部插入節點(4)刪除節點(5)頭部插入節點(6) 頭刪節點(7) 尋找節點(8) pos位置…

網絡編程socket.close/output.close/socket.shutdownOutput區別與流程分析

文章目錄 三種方法效果的區別套接字Socket關閉與釋放的區別服務器執行三種關閉操作后&#xff0c;繼續發送/接收數據會發生什么socket.shutdownOutput 關閉連接 找了半個小時沒一個說明白的帖子&#xff0c;真的折磨 三種方法效果的區別 socket.close()Socket主動禁止輸入和輸…

APP外包開發原生和H5的區別

原生開發和H5開發是兩種不同的方法&#xff0c;用于創建移動應用程序。它們具有各自的特點、優勢和劣勢&#xff0c;適用于不同的應用場景。以下是原生開發和H5開發之間的一些主要區別&#xff0c;希望對大家有所幫助。北京木奇移動技術有限公司&#xff0c;專業的軟件外包開發…

DELETE 與TRUNCATE區別

DELETE 與TRUNCATE區別 要清空 PostgreSQL 中的表數據&#xff0c;可以使用 DELETE 或 TRUNCATE 語句。下面是兩種方法的示例&#xff1a; 使用 DELETE 語句清空表數據&#xff1a; DELETE FROM 表名;例如&#xff0c;要清空名為 users 的表數據&#xff1a; DELETE FROM u…

未來公文的智能化進程

隨著技術的飛速發展&#xff0c;公文——這個有著悠久歷史的官方溝通方式&#xff0c;也正逐步走向智能化的未來。自動化、人工智能、區塊鏈...這些現代科技正重塑我們的公文制度&#xff0c;讓其變得更加高效、安全和智慧。 1.語義理解與自動生成 通過深度學習和NLP&#xff…

14-案例:購物車

綜合案例-購物車 需求說明: 1. 渲染功能 v-if/v-else v-for :class 2. 刪除功能 點擊傳參 filter過濾覆蓋原數組 3. 修改個數 點擊傳參 find找對象 4. 全選反選 計算屬性computed 完整寫法 get/set 5. 統計 選中的 總價 和 數量 計算屬性conputed reduce條件求和 6. 持久化到本…

電子商務公開密鑰加密法

(一)定義與應用原理 公開密鑰加密法是針對私有密鑰加密法的缺陷而提出來的。是電子商務應 用的核心密碼技術。所謂公開密鑰加密&#xff0c;就是指在計算機網絡上甲、乙兩用戶之間 進行通信時&#xff0c;發送方甲為了保護要傳輸的明文信息不被第三方竊取&#xff0c;采用密…

從零基礎到精通IT:探索高效學習路徑與成功案例

文章目錄 導語&#xff1a;第一步&#xff1a;明確學習目標與方向選擇適合的IT方向設定具體的學習目標咨詢和調研 第二步&#xff1a;系統學習基礎知識選擇適合的編程語言學習數據結構和算法掌握操作系統和計算機網絡基礎 第三步&#xff1a;實踐項目鍛煉技能選擇合適的項目編寫…

聊一下操作系統 macOS 與 Linux

對于Windows操作系統大家都比較熟悉&#xff0c;也常拿它與Linux操作系統進行比較&#xff0c;兩者之間的差異也很明顯。但對于macOS 和 Linux的比較不太多&#xff0c;很多人認為它們很相似&#xff0c;因為這兩種操作系統都可以運行 Unix 命令。其實詳細比較下&#xff0c;兩…

Redis——哨兵模式(docker部署redis哨兵)+緩存穿透和雪崩

哨兵模式 自動選取主機的模式。 概述 主從切換技術的方法是:當主服務器宕機后&#xff0c;需要手動把一臺從服務器切換為主服務器&#xff0c;這就需要人工干預&#xff0c;費事費力&#xff0c;還會造成段時間內服務不可用。這不是一種推薦的方式&#xff0c;更多時候&…

前端開發怎么解決性能優化的問題? - 易智編譯EaseEditing

前端性能優化是確保網站或應用在加載速度、響應性和用戶體驗等方面達到最佳狀態的關鍵任務。以下是一些解決前端性能優化問題的方法&#xff1a; 壓縮和合并代碼&#xff1a; 壓縮和合并CSS、JavaScript和HTML文件可以減少文件大小&#xff0c;加快加載速度。使用壓縮工具&am…

【Linux】Linux下常用查看文件指令小結

0x00 前言 版本信息&#xff1a;Ubuntu 18.04.6 LTS 最后更新日期&#xff1a;2023.8.18 0x01 Linux下常用查看文件指令小結 cat file &#xff1a;顯示文件內容&#xff0c;支持-n選項&#xff0c;即cat -n file&#xff0c;表示加行號顯示文件內容&#xff0c;不過不適合看…

vue vs react

vue 簡介&#xff1a;漸進式 JavaScript 框架 來源&#xff1a;最初由 Evan You &#xff08;尤雨溪&#xff09;于2014年開發。Evan You之前在Google研究過AngularJS&#xff0c;并提取了Angular的部分特性以提供一個更輕量級的框架 版本&#xff1a; vue 1x&#xff1a;2014…

協同過濾推薦算法-基于Django+mysql的智能水果銷售系統設計(可做計算機畢設)

隨著科技的不斷發展&#xff0c;智能化已經成為各行各業的趨勢&#xff0c;水果銷售行業也不例外。智能水果銷售系統就是應運而生的一種智能化解決方案&#xff0c;它可以為用戶提供更加便捷、高效的購物體驗。其中&#xff0c;系統模塊是智能水果銷售系統的重要組成部分。 系…

tsconfig.json

概念 tsconfig.json所在位置是ts項目的根目錄&#xff0c;他的主要作用是自定義配置不同的選項來告訴編譯器如何編譯當前項目。 重要屬性 compilerOptions - 主要用來配置目標js版本&#xff08;target&#xff09;、模塊解析方式&#xff08;moudle&#xff09;、輸出目錄&am…

python實現文字轉語音

文字轉語音 簡介 pyttsx3是一個Python庫&#xff0c;用于文字轉語音的功能。它可以將文本轉換為語音&#xff0c;并使用不同的音頻引擎進行輸出。這個教程將向您介紹如何使用pyttsx3來創建自定義的語音應用程序。 安裝 使用以下命令安裝pyttsx3庫&#xff1a; pip install…

unet pytorch

1.單機多卡版本&#xff1a;代碼中的DistributedDataParallel (DDP) 部分對應單機多卡的分布式訓練方式 import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torch.utils.data import Dataset, DataLoader from torchvisi…

ArcPy將矢量屬性表批量轉換為Excel文件

要使用ArcPy將矢量屬性表批量轉換為Excel文件&#xff0c;可以按照以下步驟進行操作&#xff1a; 1. 導入所需的Python庫&#xff1a; import arcpy import pandas as pd 2. 設置工作空間和要素類路徑&#xff1a;將arcpy.env.workspace設置為包含要素類的工作空間路徑&…

【Apollo學習筆記】—— Planning模塊

前言 本文記錄學習planning模塊時的一些筆記&#xff0c;總體流程參照https://zhuanlan.zhihu.com/p/61982682中的流程圖&#xff0c;如上圖所示。 planning_component modules/planning/planning_component.cc PlanningComponent::Init部分首先完成規劃模式的選擇&#xff…