1. 程式人生 > >HttpServlet背後的那些事!

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介面,其中主要完成了以下工作:

  1. 將init(ServletConfig config)方法中的config物件賦給類的成員變數,可以通過getServletConfig()獲得
  2. 為Servlet介面所有方法提供預設的實現(有的是空方法體)
  3. 提供一個新的不帶引數的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中,主要做了如下兩個改動:

  1. 重寫原始service()方法,並且重新定義了一個service(HttpServletRequest req,HttpServletResponse res),注意這裡的兩個引數變了,HttpServletRequest是ServletRequest的子類,封裝了Http相關內容,HttpServletResponse也是一樣。
  2. 在自定義的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的理解,平時在學習的時候,也應該從底層原理取理解一項技術,這樣不僅能幫助我們理解,也可以學習到很多優秀的程式設計思想,這也是大家都建議程式設計師要學會閱讀原始碼的原因之一。

  希望大家都可以在自己的道路上闖出一片天地!