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方法即可。
總述
通過對繼承體系以及程式碼實現的分析,我們可以概括為如下的邏輯圖: