servlet反射、生命周期、介面

  • 2019 年 10 月 10 日
  • 筆記

什麼是Servlet

Servlet是JavaWeb的三大組件之一,它屬於動態資源。Servlet的作用是處理請求,伺服器會把接收到的請求交給Servlet來處理,在Servlet中通常需要:

l 接收請求數據;

l 處理請求;

l 完成響應。

  例如客戶端發出登錄請求,或者輸出註冊請求,這些請求都應該由Servlet來完成處理!Servlet需要我們自己來編寫,每個Servlet必須實現javax.servlet.Servlet介面。

實現Servlet的方式(由我們自己來寫!)

實現Servlet有三種方式:

l 實現javax.servlet.Servlet介面;

l 繼承javax.servlet.GenericServlet類;

l 繼承javax.servlet.http.HttpServlet類;

  通常我們會去繼承HttpServlet類來完成我們的Servlet,但學習Servlet還要從javax.servlet.Servlet介面開始學習。

Servlet.java

public interface Servlet [c1] { public void init(ServletConfig config) throws ServletException; public ServletConfig getServletConfig(); public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; public String getServletInfo(); public void destroy(); }

Servlet中的方法大多數不由我們來調用,而是由Tomcat來調用。並且Servlet的對象也不由我們來創建,由Tomcat來創建!

public class AServlet implements Servlet {  	/*  	 * 它也是生命周期方法  	 * 它會在Servlet被銷毀之前調用,並且它只會被調用一次!  	 */  	@Override  	public void destroy() {  		System.out.println("destory()...");  	}    	/*  	 * 可以用來獲取Servlet的配置資訊  	 */  	@Override  	public ServletConfig getServletConfig() {  		System.out.println("getServletConfig()...");  		return null;  	}    	/*  	 * 獲取Servlet的資訊  	 */  	@Override  	public String getServletInfo() {  		System.out.println("getServletInfo()...");  		return "我是一個快樂的Servlet";  	}    	/*  	 * 它是生命周期方法  	 * 它會在Servlet對象創建之後馬上執行,並只執行一次!(出生之後)  	 *  	 */  	@Override  	public void init(ServletConfig servletConfig) throws ServletException {  		System.out.println("init()...");    		/*  		 * 獲取初始化參數  		 */  		System.out.println(servletConfig.getInitParameter("p1"));  		System.out.println(servletConfig.getInitParameter("p2"));    		/*  		 * 獲取所有初始化參數的名稱  		 */  		Enumeration e = servletConfig.getInitParameterNames();  		while(e.hasMoreElements()) {  			System.out.println(e.nextElement());  		}  	}    	/*  	 * 它是生命周期方法  	 * 它會被調用多次!!!  	 * 每次處理請求都是在調用這個方法!  	 */  	@Override  	public void service(ServletRequest request, ServletResponse response)  			throws ServletException, IOException {  		System.out.println("service()...");  	}  }

生命周期方法:

l void init(ServletConfig):出生之後(1次);

l void service(ServletRequest request, ServletResponse response):每次處理請求時都會被調用;

l void destroy():臨死之前(1次);

特性:

l 單例,一個類只有一個對象;當然可能存在多個Servlet類!

l 執行緒不案例的,所以它的效率是高的!

Servlet類由我們來寫,但對象由伺服器來創建,並且由伺服器來調用相應的方法。

GenericServlet

1 GenericServlet概述

GenericServlet是Servlet介面的實現類,我們可以通過繼承GenericServlet來編寫自己的Servlet。下面是GenericServlet類的源程式碼:

GenericServlet.java

public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable { private static final long serialVersionUID = 1L; private transient ServletConfig config; public GenericServlet() {} @Override public void destroy() {} @Override public String getInitParameter(String name) { return getServletConfig().getInitParameter(name); } @Override public Enumeration<String> getInitParameterNames() { return getServletConfig().getInitParameterNames(); } @Override public ServletConfig getServletConfig() { return config; } @Override public ServletContext getServletContext() { return getServletConfig().getServletContext(); } @Override public String getServletInfo() { return ""; } @Override public void init[崔1] (ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init[崔2] () throws ServletException {} public void log(String msg) { getServletContext().log(getServletName() + ": " + msg); } public void log(String message, Throwable t) { getServletContext().log(getServletName() + ": " + message, t); } @Override public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; @Override public String getServletName() { return config.getServletName(); } }

2 GenericServlet的init()方法

在GenericServlet中,定義了一個ServletConfig config實例變數,並在init(ServletConfig)方法中把參數ServletConfig賦給了實例變數。然後在該類的很多方法中使用了實例變數config。

如果子類覆蓋了GenericServlet的init(StringConfig)方法,那麼this.config=config這一條語句就會被覆蓋了,也就是說GenericServlet的實例變數config的值為null,那麼所有依賴config的方法都不能使用了。如果真的希望完成一些初始化操作,那麼去覆蓋GenericServlet提供的init()方法,它是沒有參數的init()方法,它會在init(ServletConfig)方法中被調用。

3 實現了ServletConfig介面

  GenericServlet還實現了ServletConfig介面,所以可以直接調用getInitParameter()、getServletContext()等ServletConfig的方法。


[崔1]實現了Servlet的init(ServletConfig)方法,把參數config賦給了本類的成員config,然後再調用本類自己的無參的init()方法。

[崔2]這個方法是GenericServlet自己的方法,而不是從Servlet繼承下來的。當我們自定義Servlet時,如果想完成初始化作用就不要再重複init(ServletConfig)方法了,而是應該去重寫init()方法。因為在GenericServlet中的init(ServletConfig)方法中保存了ServletConfig對象,如果覆蓋了保存ServletConfig的程式碼,那麼就不能再使用ServletConfig了。

HttpServlet

1 HttpServlet概述

HttpServlet類是GenericServlet的子類,它提供了對HTTP請求的特殊支援,所以通常我們都會通過繼承HttpServlet來完成自定義的Servlet。

2 HttpServlet覆蓋了service()方法

HttpServlet類中提供了service(HttpServletRequest,HttpServletResponse)方法,這個方法是HttpServlet自己的方法,不是從Servlet繼承來的。在HttpServlet的service(ServletRequest,ServletResponse)方法中會把ServletRequest和ServletResponse強轉成HttpServletRequest和HttpServletResponse,然後調用service(HttpServletRequest,HttpServletResponse)方法,這說明子類可以去覆蓋service(HttpServletRequest,HttpServletResponse)方法即可,這就不用自己去強轉請求和響應對象了。

其實子類也不用去覆蓋service(HttpServletRequest,HttpServletResponse)方法,因為HttpServlet還要做另一步簡化操作,下面會介紹。

HttpServlet.java

public abstract class HttpServlet extends GenericServlet { protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { …… } @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; [崔1] } catch (ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response);[崔2] } …… }

3 doGet()和doPost()

在HttpServlet的service(HttpServletRequest,HttpServletResponse)方法會去判斷當前請求是GET還是POST,如果是GET請求,那麼會去調用本類的doGet()方法,如果是POST請求會去調用doPost()方法,這說明我們在子類中去覆蓋doGet()或doPost()方法即可。

public class AServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("hello doGet()…"); } }

public class BServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("hello doPost()…"); } }


[崔1]強轉

[崔2]調用上面service()方法

Servlet細節

l 不要在Servlet中創建成員!創建局部變數即可!

l 可以創建無狀態成員!

l 可以創建有狀態的成員,但狀態必須為只讀的!

1 Servlet與執行緒安全

因為一個類型的Servlet只有一個實例對象,那麼就有可能會現時出一個Servlet同時處理多個請求,那麼Servlet是否為執行緒安全的呢?答案是:「不是執行緒安全的」。這說明Servlet的工作效率很高,但也存在執行緒安全問題!

所以我們不應該在Servlet中便宜創建成員變數,因為可能會存在一個執行緒對這個成員變數進行寫操作,另一個執行緒對這個成員變數進行讀操作。

2 讓伺服器在啟動時就創建Servlet

默認情況下,伺服器會在某個Servlet第一次收到請求時創建它。也可以在web.xml中對Servlet進行配置,使伺服器啟動時就創建Servlet。

<servlet> <servlet-name>hello1</servlet-name> <servlet-class>cn.itcast.servlet.Hello1Servlet</servlet-class> <load-on-startup>0</load-on-startup>[c1] </servlet> <servlet-mapping> <servlet-name>hello1</servlet-name> <url-pattern>/hello1</url-pattern> </servlet-mapping> <servlet> <servlet-name>hello2</servlet-name> <servlet-class>cn.itcast.servlet.Hello2Servlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hello2</servlet-name> <url-pattern>/hello2</url-pattern> </servlet-mapping> <servlet> <servlet-name>hello3</servlet-name> <servlet-class>cn.itcast.servlet.Hello3Servlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hello3</servlet-name> <url-pattern>/hello3</url-pattern> </servlet-mapping>

在<servlet>元素中配置<load-on-startup>元素可以讓伺服器在啟動時就創建該Servlet,其中<load-on-startup>元素的值必須是大於等於的整數,它的使用是伺服器啟動時創建Servlet的順序。上例中,根據<load-on-startup>的值可以得知伺服器創建Servlet的順序為Hello1Servlet、Hello2Servlet、Hello3Servlet。


[c1]在<servlet>中配置<load-on-startup>,其中給出一個非負整數!