HttpServlet背後的那些事!
閱讀本篇文章大約花費您4分鐘!
我們自己新建一個Servlet類的時候,我們通常會繼承自HttpServlet類,並且編譯器也是預設幫我們繼承了HttpServlet類,為什麼我們要繼承HttpSerlvet類呢?初學Web的同學一定知道Servlet類,並且熟知裡面的五個方法和Serlvet的生命週期,為什麼到具體使用Serlvet的時候卻和HttpServlet關係深厚,看起來和Serlvet類好像沒什麼關係?
今天我們就來看一下這其中到底是怎麼回事。我們需要了解三個類,分別是Servlet,GenericServlet,HttpServlet。
Servlet介面
Servlet中一共有五個方法,其中包含三個生命週期方法:
- init(ServletConfig config) 初始化方法【生命週期方法】
- service()方法 具體的操作方法 【生命週期方法】
- destroy()方法 銷燬方法 【生命週期方法】
- getServletInfo() 返回Serlvet描述資訊 【非生命週期方法】
- getServletConfig() 返回servlet配置物件 【非生命週期方法】
實現serlvet介面我們必須重寫這五個方法,顯然這是不友好的,所以有一個GenericServlet類對Servlet進行了封裝。
GenericServlet類
GenericServlet類是一個抽象類,實現Servlet介面,其中主要完成了以下工作:
- 將init(ServletConfig config)方法中的config物件賦給類的成員變數,可以通過getServletConfig()獲得
- 為Servlet介面所有方法提供預設的實現(有的是空方法體)
- 提供一個新的不帶引數的init()方法,子類通過重寫這個方法而不是原始的init()方法來進行初始化工作
大致的實現程式碼如下:
abstract class GenericServlet implements Servlet,ServletConfig{ private ServletConfig servletConfig; public void init(ServletConfig servletConfig) throws ServletException { this.servletConfig=servletConfig; //呼叫自定義的init() this.init(); } //自定義的init()方法,可以由子類覆蓋 public void init(){ } //實現service()空方法,並且宣告為抽象方法,強制子類必須實現service()方法 public abstract void service(ServletRequest request,ServletResponse response) throws ServletException,java.io.IOException{ } //實現空的destroy方法 public void destroy(){ } }
自定義init()方法的原因是:如果子類要初始化必須覆蓋父類的init(ServletConfig)而使它無效, this.servletConfig=servletConfig不起作用 這樣就會導致空指標異常。自定義init()後,如果子類要初始化,可以直接覆蓋不帶引數的init()方法。
不該引數的init()方法不是生命週期方法。
進行了這樣的封裝,一個Servlet繼承自GenericServlet要方便很多,但是在網路應用中,離不開Http協議,而GenericServlet中的方法還很少,無法滿足我們的使用,於是出現了HttpServlet。
HttpServlet類
HttpServlet是一個抽象類,進一步繼承了GenericServlet。它不僅進一步封裝了Serlvet,還提供了很大Http相關的方法,關於這些方法的使用都很簡單,這裡不再贅述,主要看一下HttpServlet是如何改進GenericServlet的。
在HttpServlet中,主要做了如下兩個改動:
- 重寫原始service()方法,並且重新定義了一個service(HttpServletRequest req,HttpServletResponse res),注意這裡的兩個引數變了,HttpServletRequest是ServletRequest的子類,封裝了Http相關內容,HttpServletResponse也是一樣。
- 在自定義的service()方法中,針對不同的請求分別呼叫處理不同請求的方法doGet(),doPost()
程式碼如下:
abstract class HttpServlet extends GenericServlet{
//HttpServlet中的service
protected void service(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse){
//該方法通過httpServletRequest.getMethod()判斷請求型別呼叫doGet() doPost()
}
//必須實現父類的service()方法
public void service(ServletRequest servletRequest,ServletResponse servletResponse){
HttpServletRequest request;
HttpServletResponse response;
//由於HttpSerlvetRequest/response是ServletRequest/response的子類,可以轉換
try{
request=(HttpServletRequest)servletRequest;
response=(HttpServletResponse)servletResponse;
}catch(ClassCastException){
throw new ServletException("non-http request or response");
}
//呼叫service()方法
this.service(request,response);
}
}
經過HttpSerlvet封裝之後,我們只需要針對不同的請求重寫doPost()或者doGet()方法即可,也就是我們平時在eclipse中最常使用的方式。
這就是Servlet到HttpServlet的過程,瞭解了這個過程有助於幫助Serlvet的理解,平時在學習的時候,也應該從底層原理取理解一項技術,這樣不僅能幫助我們理解,也可以學習到很多優秀的程式設計思想,這也是大家都建議程式設計師要學會閱讀原始碼的原因之一。
希望大家都可以在自己的道路上闖出一片天地!