Tomcat深入淺出——Servlet(二)

2022-07-05 18:00:26

一、Servlet簡介

Servlet類最終開發步驟:

  • 第一步:編寫一個Servlet類,直接繼承HttpServlet
  • 第二步:重寫doGet方法或者doPost方法,重寫哪個我說的算!
  • 第三步:將Servlet類設定到web.xml檔案當中
  • 第四步:準備前端的頁面(form表單),指定請求路徑即可

  • Servlet是一個介面,在它的下面有GenericServlet
    和HttpServlet兩個實現類

我們來了解一下GenericServlet:

  • 其中的GenericServlet實現了很多方法,只保留了一個抽象方法,就是我們經常用的service方法。

  • 這或許就是為了更簡潔一點吧,我們每次不需要在像實現Servlet介面一樣,將全部的抽象方法都實現。
  • 根據他的類圖可知,HttpServlet繼承了GenericServlet這個類,那麼它為什麼要繼承GenericServlet呢?

我們來了解一下HttpServlet:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
  • 這段是HttpServlet中實現的Service方法的原始碼,我們可以清楚的瞭解到,Tomcat已經把這個Service寫好了,我們只需要去實現doGet()doPost()等等這些請求的方法就可以了。
  • 因為這個Service會自己判斷,我們進行存取的是什麼請求,然後自動找到相應的方法進行處理。

二、兩種設計模式

2.1介面卡模式

介面卡:介面卡就是一種適配中介軟體,它存在於不匹配的二者之間,用於連線二者,將不匹配變得匹配,簡單點理解就是平常所見的轉接頭,轉換器之類的存在。

public interface MyInterface {
    void m1();
    void m2();
    void m3();
    void m4();
    void m5();
    void test();
    //但是這個介面中我們常用的方法只有test(),我們在實現此介面的時候,還需要實現其他方法,很累贅。
    //所以我們就需要一個介面卡!
}
public abstract class Test implements MyInterface{
    public void m1() {
    }

    public void m2() {
    }

    public void m3() {
    }

    public void m4() {
    }

    public void m5() {
    }
    //這是一個介面卡
    //在建立一個介面的實現類,我們將常用的方法設定為抽象的方法
    //這樣我們只需要繼承該類,然後實現方法即可
    public abstract void test();
}
  • 這樣做的優點就是下次我們只需要去繼承Test類,實現我們經常使用的test()方法就可以了。
  • 使我們的減少了冗餘的程式碼量
  • 同時你也會發現這個就和GenericServlet去繼承Servlet這個介面是一樣的
  • 這就是介面卡設計模式,其實很簡單,也有利於我們更加理解抽象類的作用和介面的使用
public class Realize extends Test{
    public void test() {
        //這樣我們就不需要再去實現介面中的 其他方法了
    }
}

2.2 模板設計方法模式

模板方法模式:是一種行為設計模式, 它在超類中定義了一個演演算法的框架, 允許子類在不修改結構的情況下重寫演演算法的特定步驟。

  • 模板方法模式建議將演演算法分解為一系列步驟, 然後將這些步驟改寫為方法, 最後在 「模板方法」 中依次呼叫這些方法。 步驟可以是 抽象的, 也可以有一些預設的實現。 為了能夠使用演演算法, 使用者端需要自行提供子類並實現所有的抽象步驟。 如有必要還需重寫一些步驟 (但這一步中不包括模板方法自身)。

  • 認真的去閱讀上面這段話:你就會發現他說將 演演算法進行分解,最後在模板中依次呼叫:

    • 此時的你是否想起我們剛開篇介紹的HttpServlet???
    • HttpServlet繼承了 GenericServlet類,而GenericServlet類又實現了Servlet介面;HttpServlet裡面的Service()方法你是否還記得?
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
  • 一大堆的ifelse ifelse 這不就是所謂的將演演算法分塊提煉了出來嗎?然後我們只需要去刻畫doGet()doPost()等方法,程式的主框架是不變的,只是其中的細節需要我們自己去實現。
  • 我個人目前覺得和介面卡模式比較相似,但略有不同之處。

三、Servlet物件的宣告週期

3.1 Servlet物件是由誰來維護的?

  • Servlet物件的建立、以及物件上方法的呼叫、物件的銷燬這個過程,我們JavaWeb程式設計師是無權干預的。你可以仔細想想你什麼時候new出來過一個Servlet物件。
  • Servlet物件的宣告週期是由Tomcat伺服器負責的
  • Tomcat伺服器我們又可以稱之為:WEB容器
  • WEB容器來管理Servlet物件的死活

3.2 Servlet認知強化

  • 你還真可以自己new一個Servlet物件,但是並不受我們Tomcat管理,所以你自己new出來的Servlet物件,死活和Tomcat沒有關係。
  • Tomcat建立的Servlet物件,這些Servlet都會被放到一個集合當中(HashMap),只有放到HashMap集合的Servlet才能夠被Tomcat容器管理。
  • Tomcat容器底層應該有一個HashMap這樣的集合,在這個集合當中儲存了Servlet物件和請求路徑之間的關係,我想此時你更能夠想到為什麼我們總是在xml中如此定義?

  • 我們可以將Servlet物件稱之為假單例模式,因為Servlet只有一個物件呀。