day03-實現02

来源:https://www.cnblogs.com/liyuelian/archive/2022/11/17/16901017.html
-Advertisement-
Play Games

實現02 3.實現任務階段3-處理Servlet02 3.3Servlet規範設計 3.3.1MyServlet 該類模仿Servlet介面,為了簡化,只聲明瞭三個方法:init(),service(),destroy() package com.li.MyTomcat.servlet; impor ...


實現02

3.實現任務階段3-處理Servlet02

3.3Servlet規範設計

3.3.1MyServlet

該類模仿Servlet介面,為了簡化,只聲明瞭三個方法:init(),service(),destroy()

package com.li.MyTomcat.servlet;

import com.li.MyTomcat.http.MyRequest;
import com.li.MyTomcat.http.MyResponse;

import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 * 只保留了三個核心的方法聲明
 */
public interface MyServlet {
    void init() throws Exception;

    void service(MyRequest request, MyResponse response) throws IOException;

    void destroy();
}

3.3.2MyHttpServlet

該類實現MyServlet介面,並聲明瞭doGet(),doPost()方法讓MyHttpServlet的子類去實現

package com.li.MyTomcat.servlet;

import com.li.MyTomcat.http.MyRequest;
import com.li.MyTomcat.http.MyResponse;

import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 */
public abstract class MyHttpServlet implements MyServlet {
    @Override
    public void service(MyRequest request, MyResponse response) throws IOException {
        //equalsIgnoreCase 比較字元串內容是否相同,不區分大小寫
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            //這裡會有動態綁定
            this.doGet(request, response);

        } else if ("POST".equalsIgnoreCase(request.getMethod())) {
            //這裡會有動態綁定
            this.doPost(request, response);
        }
    }

    //這裡我們使用一個模板設計模式
    //讓MyHttpServlet的子類去實現
    public abstract void doGet(MyRequest request, MyResponse response);

    public abstract void doPost(MyRequest request, MyResponse response);
}

3.3.3MyCalServlet

業務類servlet

package com.li.MyTomcat.servlet;

import com.li.MyTomcat.http.MyRequest;
import com.li.MyTomcat.http.MyResponse;
import com.li.MyTomcat.utils.WebUtils;

import java.io.IOException;
import java.io.OutputStream;

/**
 * @author 李
 * @version 1.0
 */
public class MyCalServlet extends MyHttpServlet {
    @Override
    public void doGet(MyRequest request, MyResponse response) {
        //動態綁定機制,每次調用doGet方法都會執行service方法

        //業務代碼
        //完成計算任務
        int num1 = WebUtils.parseInt(request.getParameter("num1"), 0);
        int num2 = WebUtils.parseInt(request.getParameter("num2"), 0);
        int sum = num1 + num2;
        //返回結算結果給瀏覽器
        //outputStream和當前連接的socket關聯
        OutputStream outputStream = response.getOutputStream();
        String respMes = MyResponse.respHeader + "<h1>" + num1 + "+" + num2 + "=" + sum + "</h1>";
        try {
            outputStream.write(respMes.getBytes());
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(MyRequest request, MyResponse response) {
        doGet(request, response);
    }

    @Override
    public void init() throws Exception {}

    @Override
    public void destroy() {}
}

3.3.4WebUtils

該類為工具類,供MyCalServlet使用

package com.li.MyTomcat.utils;

public class WebUtils {
    public static int parseInt(String str, int defaultVal) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            System.out.println(str + " 不能轉成數字");
        }
        return defaultVal;
    }
}

3.3.5RequestHandler

修改RequestHandler類(線程類),如下:

package com.li.MyTomcat.hander;

import com.li.MyTomcat.http.MyRequest;
import com.li.MyTomcat.http.MyResponse;
import com.li.MyTomcat.servlet.MyCalServlet;

import java.io.*;
import java.net.Socket;

/**
 * @author 李
 * @version 1.0
 * RequestHandler是一個線程對象
 * 用來處理一個http請求
 */
public class RequestHandler implements Runnable {
    //定義一個Socket
    private Socket socket = null;

    //在創建RequestHandler對象的時候,將主線程的socket傳給線程對象來使用
    public RequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //對客戶端進行交互

        try {
            System.out.println("當前線程=" + Thread.currentThread().getName());

            InputStream inputStream = socket.getInputStream();

            MyRequest myRequest = new MyRequest(inputStream);

            //這裡我們可以通過myResponse對象返回數據給客戶端
            MyResponse myResponse = new MyResponse(socket.getOutputStream());
            //創建MyCalServlet對象(之後再用反射來構建對象,而不是new)
            MyCalServlet myCalServlet = new MyCalServlet();
            //之後再用反射來調用方法
            myCalServlet.doGet(myRequest, myResponse);

            inputStream.close();
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //一定要確保socket關閉
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

MyTomcatV2類不變

3.3.6測試

運行MyTomcatV2,在瀏覽器中輸入地址http://localhost:8080/myCalServlet?num1=100&num2=200,回車,瀏覽器顯示如下:

image-20221117175614650

3.4容器設計

image-20221117180011550

在上面的線程類RequestHandler中,我們是通過new來創建一個 實現了Servlet介面 的對象,但是實際的tomcat是反射來實例化該對象的。

一個問題:Servlet應該在哪裡被實例化,以及如何去管理呢?

思路:

image-20221117185516073

將來的Servlet會很多,我們不可能去到MyTomcat源碼裡面去修改,應該用web.xml文件去配置Servlet

為了管理,應該創建兩個容器(使用HashMap或ConcurrentHashMap)

一個容器(ServletMapping)存放 :HashMap<ServletName,對應的實例>

另一個容器(ServletUrlMapping)存放:HashMap<url-pattern,ServletName>

當瀏覽器發送請求,我們獲取該請求的uri部分,去容器ServletUrlMapping裡面去查該uri,如果有,通過該uri獲取對應的ServletName。然後到另一個容器ServletMapping,根據ServletName去找對應的反射對象的實例,然後再去調用它。

3.4.1容器實現

  1. 首先在web.xml文件中配置自己設計的Servlet
<!--配置自己設計的Servlet-->
<servlet>
    <servlet-name>MyCalServlet</servlet-name>
    <!--忽略報錯-->
    <servlet-class>com.li.MyTomcat.servlet.MyCalServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>MyCalServlet</servlet-name>
    <url-pattern>/myCalServlet</url-pattern>
</servlet-mapping>

註意:<servlet-class>這裡會報錯,因為非原生的servlet,xml不會識別,我們忽l略報錯即。

記得在target\classes目錄手動拷貝一份web.xml [預設是自動拷貝,這裡不是原生tomcat,因此不會自動拷貝],配置信息可以從上一個項目web.xml拷貝修改即可。所有報紅都忽略

image-20221117192905568
  1. 註意在pom.xml文件中導入dom4j的jar包:
<!--導入dom4j-->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.1</version>
</dependency>
image-20221117193104640
  1. MyTomcatV3
package com.li.MyTomcat;

import com.li.MyTomcat.hander.RequestHandler;
import com.li.MyTomcat.servlet.MyHttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 李
 * @version 3.0
 * 第三版MyTomcat,實現通過xml和反射來初始化容器
 * (這裡設置為一啟動MyTomcat就實例化servlet)
 */
public class MyTomcatV3 {
    //1.容器 ServletMapping 存放 ConcurrentHashMap<ServletName,對應的實例>
    public static final ConcurrentHashMap<String, MyHttpServlet>
            servletMapping = new ConcurrentHashMap<>();

    //2.容器 ServletUrlMapping 存放 ConcurrentHashMap<url-pattern,ServletName>
    public static final ConcurrentHashMap<String, String>
            servletUrlMapping = new ConcurrentHashMap<>();

    public static void main(String[] args) {
        MyTomcatV3 myTomcatV3 = new MyTomcatV3();
        myTomcatV3.init();
        //啟動MyTomcatV3容器
        myTomcatV3.run();
    }

    //啟動MyTomcatV3容器
    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("=====MyTomcatV3在8080監聽=====");
            while (!serverSocket.isClosed()) {
                Socket socket = serverSocket.accept();
                RequestHandler requestHandler = new RequestHandler(socket);
                new Thread(requestHandler).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //直接對兩個容器進行初始化
    public void init() {
        //讀取web.xml文件 => dom4j(註意導jar包)

        //path=/D:/IDEA-workspace/li_tomcat/target/classes/
        String path = MyTomcatV3.class.getResource("/").getPath();

        //使用dom4j技術完成web.xml文件的讀取
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(new File(path + "web.xml"));
            //得到根元素
            Element rootElement = document.getRootElement();
            //得到根節點下麵的所有元素
            List<Element> elements = rootElement.elements();
            //遍歷並過濾到servlet和servlet-mapping
            for (Element element : elements) {
                //如果元素為servlet
                if ("servlet".equalsIgnoreCase(element.getName())) {
                    //這是一個servlet配置
                    //System.out.println("發現servlet");
                    //使用反射將該servlet實例放入到容器servletMapping中
                    String servletName = element.element("servlet-name").getText().trim();
                    String servletClass = element.element("servlet-class").getText().trim();
                    servletMapping.put(servletName,
                            (MyHttpServlet) Class.forName(servletClass).newInstance());

                } else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
                    //如果元素為servlet-mapping
                    //System.out.println("發現servlet-mapping");
                    String servletName = element.element("servlet-name").getText().trim();
                    String urlPattern = element.element("url-pattern").getText().trim();
                    //將uri對應的servletName放入容器servletUrlMapping中
                    servletUrlMapping.put(urlPattern, servletName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

修改RequestHandler:

package com.li.MyTomcat.hander;

import com.li.MyTomcat.MyTomcatV3;
import com.li.MyTomcat.http.MyRequest;
import com.li.MyTomcat.http.MyResponse;
import com.li.MyTomcat.servlet.MyHttpServlet;

import java.io.*;
import java.net.Socket;

/**
 * @author 李
 * @version 1.0
 * RequestHandler是一個線程對象
 * 用來處理一個http請求
 */
public class RequestHandler implements Runnable {
    //定義一個Socket
    private Socket socket = null;

    //在創建RequestHandler對象的時候,將主線程的socket傳給線程對象來使用
    public RequestHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //對客戶端進行交互

        try {
            System.out.println("當前線程=" + Thread.currentThread().getName());

            MyRequest myRequest = new MyRequest(socket.getInputStream());

            //這裡我們可以通過myResponse對象返回數據給客戶端
            MyResponse myResponse = new MyResponse(socket.getOutputStream());

            //得到瀏覽器發送的http的uri,作為key,去查找容器里該servletName
            //然後去另一容器里找實例,如有就調用,如果沒有就返回404
            //1.得到uri=>就是servletUrlMapping的ur-pattern
            String uri = myRequest.getUri();
            String servletName = MyTomcatV3.servletUrlMapping.get(uri);
            if (servletName == null) {
                servletName = "";
            }
            //2.通過uri->servletName->servlet的實例
            //註意:這裡myHttpServlet的編譯類型是MyHttpServlet,但是運行類型是MyHttpServlet的子類
            MyHttpServlet myHttpServlet = MyTomcatV3.servletMapping.get(servletName);
            //3.調用service方法,通過動態綁定機制,調用運行類型的doGet或doPost方法
            if (myHttpServlet != null) {//如果找到到該servlet了
                myHttpServlet.service(myRequest, myResponse);
            } else {
                //沒有這個servlet,返回404
                String resp = MyResponse.respHeader + "<h1>404 Not Found</h1>";
                OutputStream outputStream = myResponse.getOutputStream();
                outputStream.write(resp.getBytes());
                outputStream.flush();
                outputStream.close();
            }

            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //一定要確保socket關閉
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

為了測試,在MyCalServlet類中增加提示信息:

image-20221117205240071

其他類保持不變

3.4.2測試

運行MyTomcatV3,在瀏覽器輸入 http://localhost:8080/myCalServlet?num1=99&num2=1

瀏覽器顯示如下:

image-20221117204334128

在地址欄中輸入不存在的資源,返回404NotFound

image-20221117204530305

後臺輸出:

image-20221117205146286

4.練習

分析html等靜態資源,完成通過頁面提交數據進行計算,瀏覽器請求 http://localhost:8080/hspCalServlet, 提交數據,完成計算任務,如果該servlet 不存在,返回 404

image-20221117210049615 image-20221117210100774 image-20221117210152605

練習

思路:線上程類中,拿到瀏覽器請求的uri的時候,增加新的業務邏輯:
(1)判斷uri是什麼資源
(2)如果是靜態資源,就讀取該資源,並返回給瀏覽器(註意設置Content-Type text/html)
(3)因為是自定義的tomcat,所以maven項目的target不會自動更新
(4)需要自己將靜態文件等拷貝到該目錄下,瀏覽器才能訪問到(這裡把讀取的靜態資源放到target/classes/下)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 視頻通話SDK用的即構的,uniapp插件市場地址 推送用的極光的,uniapp插件市場地址 即構音視頻SDK uniapp插件市場的貌似是有些問題,導入不進項目,直接去官網下載,然後放到項目下的 nativeplugins 目錄下,在配 ...
  • 在工作流頁面中,除了特定的業務表單信息外,往往也需要同時展示通用申請單的相關信息,因此在頁面設計的時候需要使用一些組件化的概念來實現動態的內容展示處理,本篇隨筆介紹Vue3+TypeScript+ElementPus的前端工作流模塊中實現統一的表單編輯和表單詳情查看處理。 ...
  • 前言: 昨天我們學習了 TS 的數據類型,不知道大家回去以後練習沒練習,如果你練習了一定會發現一個問題,我們的 TS 好像和 JS 不太一樣 JS 寫完之後直接就可以放到頁面上,就可以用了,而我們的 TS 需要用 tsc 編譯一下,編譯為 JS 才能在頁面中使用 這時就會有同學說了,誒呀,六扇老師, ...
  • 限流,通常講就是限制流量,也有很多其他的說法,比如:限頻、疲勞度控制等。 原文鏈接:自定義開發限流組件 之 場景需求分析-一隻小Coder 最近遇到一個需求,系統A作為一個專門推送消息給客戶的消息中心系統,對於每個客戶是否能接受消息,能接受多少消息,接收消息的速度,能接受哪些消息等都要進行控制,這也 ...
  • 5.4 介面開發-根據id刪除附件 第2-1-2章 傳統方式安裝FastDFS-附FastDFS常用命令 第2-1-3章 docker-compose安裝FastDFS,實現文件存儲服務 第2-1-5章 docker安裝MinIO實現文件存儲服務-springboot整合minio-minio全網最 ...
  • 自己的客服系統做好了,官網頁面也有了,但是沒有介紹性的內容文章。網站被收錄的太少,這樣會導致網站的權重不高,搜索排名比較低。 因此要簡單的加上一個小型的內容管理功能。 設計資料庫 很簡單的兩張表,分類表和內容表 DROP TABLE IF EXISTS `cms_cate`; CREATE TABL ...
  • jdk線程池工作原理解析(二) 本篇博客是jdk線程池ThreadPoolExecutor工作原理解析系列博客的第二篇,在第一篇博客中從源碼層面分析了ThreadPoolExecutor在RUNNING狀態下處理任務的核心邏輯,而在這篇博客中將會詳細講解jdk線程池ThreadPoolExecuto ...
  • RabbitMQ 常見問題 昔我往矣,楊柳依依。今我來思,雨雪霏霏。 1、什麼是RabbitMQ? RabbitMQ是一款開源的、Erlang編寫的消息中間件;最大的特點就是消費並不需要確保提供方存在,實現了服務之間的高度解耦,可以用它來:解耦、非同步、削峰。 2、MQ的優點 非同步處理 - 相比於傳統 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...