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
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...