1. 程式人生 > 其它 >Servlet類的繼承體系以及原始碼分析

Servlet類的繼承體系以及原始碼分析

Servlet類的繼承體系以及原始碼分析

寫在前面

Servlet是我開始接觸javaweb後的第一個難點,當時課程設定在大二,在Java基礎匆忙結課後就開始學習Javaweb,Servlet所涉及的一些方法以及原理完全沒有仔細瞭解過。寫這篇筆記的目的就是用現有的知識對servlet加深理解,也是對知識對複習筆記

Servlet的繼承體系

首先我在IDEA中新建了一個web專案匯入了Servlet的jar包,通過 Ctrl+H 可以得到下圖的目錄結構:

可以看到,最外層的目錄項為Interface,即Servlet介面。然後二級目錄中,GenericServlet實現了Interfa Servlet,這就是我們常在編寫類寫的implement Servlet。 在日常使用中,我們常用到getMethod()方法,而在繼承類中呼叫該方法是會報錯的,因為servlet實現的方法的父類是圖中的三級目錄:HttpServlet實現的,你無法通過呼叫抽象類的父類來實現抽象類中的方法。所以就出現了我們日常使用的:

HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

不止如此,GenericService在實現interface時定義了很多空實現,我們可以通過httpServlet繼承來實現其中service(),即請求分發。

而通過上述方式來繼承HttpServlet就可以來實現我們自己的方法。
所以概括來說,Servlet的繼承體系大概如下圖所示:


原始碼分析

Interface Servlet

我們首先來看Servlet介面:


package javax.servlet;

import java.io.IOException;

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

它定義了Servlet所能實現的操作。

GenericServlet

然後再來看GenericServlet,可以看到都是Servlet介面的實現,返回值都是一些空值或者引數,並且引入了ServletConfig來實現一些方法:


package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getInitParameter(String name) {
        return this.getServletConfig().getInitParameter(name);
    }

    public Enumeration<string> getInitParameterNames() {
        return this.getServletConfig().getInitParameterNames();
    }

    public ServletConfig getServletConfig() {
        return this.config;
    }

    public ServletContext getServletContext() {
        return this.getServletConfig().getServletContext();
    }

    public String getServletInfo() {
        return "";
    }

    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }

    public void init() throws ServletException {
    }

    public void log(String message) {
        this.getServletContext().log(this.getServletName() + ": " + message);
    }

    public void log(String message, Throwable t) {
        this.getServletContext().log(this.getServletName() + ": " + message, t);
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}

其中,可以看到service()方法被定義為抽象方法,作為唯一,且最重要的抽象方法,其存在的意義就是:誰來繼承該類,誰就要來實現父類的方法,即實現介面的功能。那麼誰來繼承GenericServlet?就是剛才結構中所提到的HttpServlet:

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 {

它所實現的,就是對請求方式的獲取和判斷,即根據請求方法來分發。在上面的程式碼中可以看到在獲取到請求後通過字串匹配來判斷,並且通過if語句執行對應的方法,這就是請求分發。

這裡我們就以上面的GET和POST請求為例,當判斷到請求方式為GET或POST後,執行doGet(),doPost()方法:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_get_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
    }
<!------------------------------->
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_post_not_supported");
        this.sendMethodNotAllowed(req, resp, msg);
    }

他們都執行了sendMethodNotAllowed()

private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
        String protocol = req.getProtocol();
        if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

而這個方法,只是丟擲了異常來指明是否支援該型別請求。
在我們實際生產中,我們接收請求後需要發給對應的方法來執行特定的業務邏輯,那麼我們在自定義類中繼承HttpServlet後,只需要通過重寫doGet和doPost方法即可。

總述

通過對繼承體系以及程式碼實現的分析,我們可以概括為如下的邏輯圖: