1. 程式人生 > >Servlet及相關類和接口

Servlet及相關類和接口

lang sage troy void inf lis 類初始化 eight spc

上一篇介紹了在Web項目中web.xml文件的配置信息,本篇主要介紹裏面非常重要的配置——Servlet配置,重點介紹與Servlet相關的幾個接口和類,包括Servlet接口、ServletConfig接口、ServletContext接口、GenericServlet類、HttpServlet類。

1、Servlet介紹

什麽是Servlet:

百度的解釋是“小服務程序或服務連接器,用Java編寫的服務器端程序,主要功能在於交互式地瀏覽和修改數據,生成動態Web內容”。Servlet是Java語言裏面的一個接口,任何直接或間接實現了Servlet接口的類都可稱為Servlet類。

Servlet與Tomcat的關系:

Tomcat 是Web應用服務器,是Servlet的容器。Tomcat 作為Servlet容器,在啟動時可以創建Servlet類的實例,調用init()方法對Servlet進行初始化,同時負責處理客戶請求,把請求傳送給Servlet,並將Servlet的響應傳送回給客戶。可以說,Servlet是一種運行在支持Java語言的服務器上的組件。在web.xml文件中,通過<servlet>標簽配置Servlet類的相關信息,詳細配置請見 http://www.cnblogs.com/Y-oung/p/8401549.html 。

2、Servlet接口

先看源碼:

技術分享圖片
package javax.servlet;

import java.io.IOException;

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

    public abstract ServletConfig getServletConfig();

    public abstract void service(ServletRequest paramServletRequest,
            ServletResponse paramServletResponse) throws ServletException,
            IOException;

    public abstract String getServletInfo();

    public abstract void destroy();
}
技術分享圖片

init():初始化方法,在Tomcat對Servlet實例化後,Servlet容器會調用init()方法來初始化該對象,讓Servlet對象在處理客戶請求前可以完成一些初始化工作,例如:建立數據庫的連接,獲取配置信息等。在Servlet的整個生命周期中,init()方法自始至終只會被調用一次。init()方法有一個類型為ServletConfig的參數,Servlet容器通過這個參數向Servlet傳遞配置信息。Servlet使用ServletConfig對象從web.xml文件中獲取以名-值對形式提供的初始化參數。另外,在Servlet中,還可以通過ServletConfig對象獲取描述Servlet運行環境的ServletContext對象,使用該對象,Servlet可以和它的Servlet容器進行通信。

技術分享圖片如圖中紅圈裏面的配置信息可以在MainServlet類的init()方法裏面解析。

getServletConfig():該方法返回容器調用init()方法時傳遞給Servlet對象的ServletConfig對象,ServletConfig對象包含了Servlet的初始化參數(如上圖紅圈裏的配置信息)。關於ServletConfig接口下面會介紹。

service():該方法是用來處理客戶端請求(在service()方法被容器調用之前,必須確保init()方法正確完成)的核心方法。容器會構造一個表示客戶端請求信息的請求對象(類型為ServletRequest)和一個用於對客戶端進行響應的響應對象(類型為ServletResponse)作為參數傳遞給service()。在service()方法中,Servlet對象通過ServletRequest對象得到客戶端的相關信息和請求信息,在對請求進行處理後,調用ServletResponse對象的方法設置響應信息。

getServletInfo():該方法返回一個包括了關於Servlet的信息的字符串(如作者、版本和版權等信息),返回的應該是純文本字符串,而不是任何類型的標記。

destroy():該方法用來銷毀Servlet對象。當容器檢測到一個Servlet對象應該從服務中被移除的時候,容器會調用該對象的destroy()方法,以便讓Servlet對象可以釋放它所使用的資源,該方法同樣只會執行一次。當需要釋放內存或者容器關閉時,容器就會調用Servlet對象的destroy()方法,在Servlet容器調用destroy()方法前,如果還有其他的線程正在service()方法中執行容器會等待這些線程執行完畢或者等待服務器設定的超時值到達。一旦Servlet對象的destroy()方法被調用,容器不回再把請求發送給該對象。如果需要改Servlet再次為客戶端服務,容器將會重新產生一個Servlet對象來處理客戶端的請求。在destroy()方法調用之後,容器會釋放這個Servlet對象,在隨後的時間內,該對象會被java的垃圾收集器所回收。

可以看到,Servlet接口裏面只有五個方法:init()、getServletConfig()、service()、getServletInfo()、destroy()。其中init()、service()、destroy()三個方法是和servlet生命周期相關的方法,在生命周期的不同時間段內會執行不同的方法,其順序是init()->service()->destroy()。其中init()、destroy()方法在servlet生命周期中都只執行一次,而service()方法可以多次執行。Servlet接口是Servlet類最底層、最基礎、最重要的接口。

3、ServletConfig接口

先看源碼:

技術分享圖片
package javax.servlet;

import java.util.Enumeration;

public abstract interface ServletConfig {
    public abstract String getServletName();

    public abstract ServletContext getServletContext();

    public abstract String getInitParameter(String paramString);

    public abstract Enumeration<String> getInitParameterNames();
}
技術分享圖片

getServletName():獲取web.xml文件裏面的servlet的名字(即<servlet-name>標簽配置的名字)。

getServletContext():獲取ServletContext對象,下面會介紹ServletContext接口。

getInitParameter():獲取web.xml文件在servlet標簽中配置的參數值(註意是servlet標簽,而不是其他標簽配置的參數值)。

getInitParameterNames():獲取在Servlet中所有初始化參數的名字,也就是key值,可以通過key值,來找到各個初始化參數的value值(註意返回的是枚舉類型)。

技術分享圖片

可以看到,ServletConfig接口裏面的方法主要是獲取web.xml文件裏和servlet相關的配置信息。

4、ServletContext接口

先看源碼:

技術分享圖片
package javax.servlet;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Map;
import java.util.Set;
import javax.servlet.descriptor.JspConfigDescriptor;

public abstract interface ServletContext {
    public static final String TEMPDIR = "javax.servlet.context.tempdir";
    public static final String ORDERED_LIBS = "javax.servlet.context.orderedLibs";

    public abstract ServletContext getContext(String paramString);

    public abstract String getContextPath();

    public abstract int getMajorVersion();

    public abstract int getMinorVersion();

    public abstract int getEffectiveMajorVersion();

    public abstract int getEffectiveMinorVersion();

    public abstract String getMimeType(String paramString);

    public abstract Set<String> getResourcePaths(String paramString);

    public abstract URL getResource(String paramString)
            throws MalformedURLException;

    public abstract InputStream getResourceAsStream(String paramString);

    public abstract RequestDispatcher getRequestDispatcher(String paramString);

    public abstract RequestDispatcher getNamedDispatcher(String paramString);

    public abstract Servlet getServlet(String paramString)
            throws ServletException;

    public abstract Enumeration<Servlet> getServlets();

    public abstract Enumeration<String> getServletNames();

    public abstract void log(String paramString);

    public abstract void log(Exception paramException, String paramString);

    public abstract void log(String paramString, Throwable paramThrowable);

    public abstract String getRealPath(String paramString);

    public abstract String getServerInfo();

    public abstract String getInitParameter(String paramString);

    public abstract Enumeration<String> getInitParameterNames();

    public abstract boolean setInitParameter(String paramString1,
            String paramString2);

    public abstract Object getAttribute(String paramString);

    public abstract Enumeration<String> getAttributeNames();

    public abstract void setAttribute(String paramString, Object paramObject);

    public abstract void removeAttribute(String paramString);

    public abstract String getServletContextName();

    public abstract ServletRegistration.Dynamic addServlet(String paramString1,
            String paramString2);

    public abstract ServletRegistration.Dynamic addServlet(String paramString,
            Servlet paramServlet);

    public abstract ServletRegistration.Dynamic addServlet(String paramString,
            Class<? extends Servlet> paramClass);

    public abstract <T extends Servlet> T createServlet(Class<T> paramClass)
            throws ServletException;

    public abstract ServletRegistration getServletRegistration(
            String paramString);

    public abstract Map<String, ? extends ServletRegistration> getServletRegistrations();

    public abstract FilterRegistration.Dynamic addFilter(String paramString1,
            String paramString2);

    public abstract FilterRegistration.Dynamic addFilter(String paramString,
            Filter paramFilter);

    public abstract FilterRegistration.Dynamic addFilter(String paramString,
            Class<? extends Filter> paramClass);

    public abstract <T extends Filter> T createFilter(Class<T> paramClass)
            throws ServletException;

    public abstract FilterRegistration getFilterRegistration(String paramString);

    public abstract Map<String, ? extends FilterRegistration> getFilterRegistrations();

    public abstract SessionCookieConfig getSessionCookieConfig();

    public abstract void setSessionTrackingModes(
            Set<SessionTrackingMode> paramSet) throws IllegalStateException,
            IllegalArgumentException;

    public abstract Set<SessionTrackingMode> getDefaultSessionTrackingModes();

    public abstract Set<SessionTrackingMode> getEffectiveSessionTrackingModes();

    public abstract void addListener(String paramString);

    public abstract <T extends EventListener> void addListener(T paramT);

    public abstract void addListener(Class<? extends EventListener> paramClass);

    public abstract <T extends EventListener> T createListener(
            Class<T> paramClass) throws ServletException;

    public abstract void declareRoles(String[] paramArrayOfString);

    public abstract ClassLoader getClassLoader();

    public abstract JspConfigDescriptor getJspConfigDescriptor();
}
技術分享圖片

Tomcat為每個web項目都創建一個ServletContext實例,在Tomcat啟動時創建,服務器關閉時銷毀,在一個web項目中共享數據,管理web項目資源,為整個web配置公共信息等。每一個web項目都存在一個ServletContext實例,每個Servlet都可以訪問它。可以看到,ServletContext接口裏面的方法很多,下面根據功能介紹其中的方法。

在Web應用範圍內存取共享數據的方法:

setAttribute(String paramString, Object paramObject):把一個Java對象與一個屬性名綁定,並把它存入到ServletContext中。paramString參數指定屬性名,paramObject參數標識共享的數據。
getAttribute(String name):根據參數給定的屬性名,返回一個Object類型的對象,它表示ServletContext中與屬性名匹配的屬性值。
getAttributeNames():返回一個Enumeration對象,該對象包含了所有存放在ServletContext中的屬性名。
removeAttribute(String name):根據參數指定的屬性名,從ServletContext中刪除匹配的屬性。

訪問當前Web應用的資源:
getContextPath():返回當前Web應用的URL入口。
getInitParameter(String name):根據給定的參數名,返回Web應用範圍內初始化參數值。在web.xml文件中,直接在<web-app>跟元素下定義的<context-param>元素表示應用範圍內的初始化參數。
getInitParameterNames():返回一個Enumeration對象,它包含了Web應用範圍內的所有初始化參數。
getServletContextName():返回Web應用的名字,即web.xml文件中<display-name>元素的值。
getRequestDispatcher(String path):返回一個用於向其他Web組件轉發請求的RequestDispatcher對象。

訪問Servlet容器中的其他Web應用:
getContext(String uripath):根據參數指定的URI,返回當前Servlet容器中其他Web應用的ServletContext對象。
訪問Servlet容器的相關信息:
getMajorVersion():返回Servlet容器支持的Java Servlet API的主版本號。
getMinorVersion();返回Servlet容器支持的Java Servlet API的次版本號。
getServletInfo():返回Servlet容器的名字和版本。

訪問服務器端的文件系統資源:
getRealPath(String path):根據參數指定的虛擬路徑,返回文件系統中的一個真實路徑。
getResource(String path):返回一個映射到參數指定的路徑的URL。
getResourceAsStream(String path):返回一個用於讀取參數指定的文件的輸入流。
getMimeType(String file):返回參數指定的文件的MIME類型。

輸出日誌:
log(String msg)向Servlet的日誌文件中寫日誌。
log(String message , java.lang.Throwable throwable):向Servlet的日誌中寫錯誤日誌,以及異常的堆棧信息。

技術分享圖片

5、GenericServlet類

先看源碼:

技術分享圖片
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 void destroy() {
    }

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

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

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

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

    public String getServletInfo() {
        return "";
    }

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

    public void init() throws ServletException {
    }

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

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

    public abstract void service(ServletRequest paramServletRequest,
            ServletResponse paramServletResponse) throws ServletException,
            IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
}
技術分享圖片

可以看到,GenericServlet類是一個抽象的(abstract)類,所以它無法被實例化。它繼承了三個接口,其中Servlet和ServletConfig接口是前面介紹過的,可以看出GenericServlet類裏面大多數方法都是這兩個接口裏面的方法,所以這個類其實是整合了這兩個接口,給出了設計servlet的一些骨架,定義了servlet生命周期,還有一些得到名字、配置、初始化參數的方法,使得編寫servlet類時更方便。GenericServlet類裏面所有繼承了這兩個接口的方法的功能,在本文前面已經詳細介紹,這裏只說明一下這個類裏面新增的方法:log(String msg)、log(String message, Throwable t)、init()。

兩個log()方法:通過ServletContext對象將Servlet的類名和給定的信息寫入log文件中。

兩個init()方法:init(ServletConfig config)方法是實現了Servlet接口中的方法,作用是可以通過getServletConfig()方法獲取ServletConfig對象。如果你覆蓋了這個方法,你必須調用super.init(config),這樣GenericServlet類的其他方法才能正常工作。而它下面還調用了無參的init()方法,也就是GenericServlet類自定義的無參方法。為什麽還要定義這個無參數的init()方法呢?其實這個無參數的init()方法是專門留給開發者使用的,開發者只需要覆蓋這個方法就可以很方便的在方法裏面擴充一些功能,比如對自己寫的Servlet類初始化時可以在這個方法裏面進行,解析web.xml文件裏面關於servlet配置的信息時可以在這個方法裏面。這樣就不需要覆蓋init(ServletConfig config)方法了,不需要存儲config對象,也不需要調用super.init(config)。

盡管GenericServlet類實現了Servlet和ServletConfig接口的功能,但它的service方法中的參數還是ServletRequest,ServletResponse,它處理的還只一般的Servlet請求,並沒有跟http相關對象掛鉤。service方法是和應用層協議無關的,也就是說你有可能用非http協議實現它。而我們平時通過瀏覽器訪問網站時,基本上都是HTTP請求(當然還有FTP請求),請求方式包括GET、POST等,顯然如果直接使用service方法會很繁瑣,所以提供了一個HttpServlet類來解決這些問題,這個類繼承了GenericServlet類,除了能實現其所有方法,還根據HTTP請求的特性進行了擴充,使得其更能有效地處理基於HTTP協議的請求。

6、HttpServlet類

這個類的源碼有點長,不再詳細展示,有興趣可以去看源碼,這裏通過類的結構圖說明。

技術分享圖片

其中HTTP協議的請求類型與其對應的實現方法見下表:技術分享圖片

該類繼承了GenericServlet類,用於接收基於Http協議的請求,並對請求進行處理,然後做出響應。一般在web開發中,開發者自己編寫的Servlet類只需要繼承HttpServlet類即可。這裏我們重點介紹兩個service()方法。

service(ServletRequest req, ServletResponse res):通過源碼可以發現,此方法是GenericServlet類的service()方法的實現,他把ServletRequest和ServletResponse對象分別強制轉化為HttpServletRequest和HttpServletResponse對象,並把強制轉化後的對象作為參數,調用另一個重載的service(HttpServletRequest req, HttpServletResponse resp)方法。

service(HttpServletRequest req, HttpServletResponse resp):通過源碼可以看出,這個方法會根據不同的HTTP請求類型(GET、POST或其他),調用不同的實現方法(doGet()、doPost()或其他)。這樣一來,當開發者自己編寫Servlet類時(繼承HttpServlet類),我們只需要覆蓋(重寫)doGet()、doPost()或其他doXXX()方法就可以了,而不需要覆蓋(重寫)service()方法,這樣就簡化了許多。實際上筆者在開發中遇到的幾個項目的servlet類中都是覆蓋(重寫)了doGet()、doPost()方法,而沒有覆蓋(重寫)service()方法。

7、總結

本篇著重介紹了和servlet相關的幾個比較重要的接口和類,下面通過圖片說明它們之間的繼承關系:

技術分享圖片

在實際的web項目中,如果需要開發者自己寫servlet類,一般直接繼承HttpServlet類即可。在用Tomcat加載servlet類時,如果需要初始化一些信息(比如解析web.xml文件中<servlet>標簽下的配置信息),可以覆蓋(重寫)無參的init()方法;如果要處理GET請求,可以覆蓋(重寫)doGet()方法;如果要處理POST請求,可以覆蓋(重寫)doPost()方法。init()、doGet()、doPost()這三個方法是筆者在開發web項目中遇到最多的,基本上自己寫的servlet類裏面,都是繼承了HttpServlet類,覆蓋(重寫)了這三個方法。

Servlet及相關類和接口