【JavaEE】Servlet介面、ServletConfig介面、GenericServlet抽象類、ServletContext介面、HttpServlet類原始碼及方法

Windsor90發表於2017-02-06

Servlet介面、ServletConfig介面、GenericServlet抽象類、ServletContext介面、HttpServlet類原始碼及方法

1.Servlet介面

Servlet介面定義的方法
【生命週期】
1.int()初始化Servlet。
2.service(), 用來處理request請求。
3.destroy(),將servlet移除容器,然後GC垃圾回收。

【addition methods】
4.getServletInfo()允許servlet返回關於servlet自身的基本資訊,如author,version,copyright
5.getServletConfig(),返回ServletConfig例項,是一個servlet configuration object 。

public interface Servlet {
  public void init(ServletConfig config);
  public ServletConfig getServletConfig();
  public void service(ServletRequest req, ServletResponse res);
  public String getServletInfo();
  public void destroy();
}

2.ServletConfig介面

Servlet容器使用的servlet設定資訊的物件,用來在初始化期間向servlet傳遞資訊
1.getServletName()返回Servlet例項的名稱。
2.getServletContext()返回ServletContext的引用。
3.getInitParameter()返回包含指定name的servlet初始化引數的值。
4.getInitParameterNames()返回初始化引數的所有name,將其包裝在Enumeration物件。

 public interface ServletConfig {
     public String getServletName();
     public ServletContext getServletContext();

     public String getInitParameter(String name);
     public Enumeration<String> getInitParameterNames(); 
}

3.GenericServlet抽象類

定義一個通用的,協議無關的Servlet類,直接繼承GenericServlet抽象類,使得我們寫servlet更容易。
通常特定協議的Servlet繼承此GenericServlet。如HttpServlet就是繼承自GenericServlet實現的。
GenericServlet實現了Servlet、ServletConfig、Serializable介面。


public abstract class GenericServlet implements Servlet,ServletConfig,Serializable{
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings =ResourceBundle.getBundle(LSTRING_FILE);
    private transient ServletConfig config;
    public GenericServlet() { }
    /**1.實現的Servlet介面*/
    public void init() throws ServletException { }  
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    public ServletConfig getServletConfig() {
        return config;
    }   
    public abstract void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException;   
    public String getServletInfo() {
        return "";
    }
    public void destroy() {}
    /**2.實現ServletConfig介面*/
    public String getServletName() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
        return sc.getServletName();
    }       
    public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
        return sc.getServletContext();
    }   
    public String getInitParameter(String name) {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
        return sc.getInitParameter(name);
    }
    public Enumeration<String> getInitParameterNames() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
        return sc.getInitParameterNames();
    }   

    /**3.實現log方法,此方法定義在ServletContext介面中*/
    public void log(String msg) {
        getServletContext().log(getServletName() + ": "+ msg);
    }
    public void log(String message, Throwable t) {
        getServletContext().log(getServletName() + ": " + message, t);
    }
}   

4.ServletContext介面

ServletContext定義一系列方法用來與Servlet容器進行分互動。如:獲取MIME型別的檔案,dispatch Request,寫入log檔案。
每一個Web應用,每個JVM中只有一個context,

public interface ServletContext { 
    /**1.Path相關操作。
        getRealPath(String path):通過給定的virtual path返回對應的real path.
        getRequestDispatcher(String path):返回給定path的servlet的RequestDispatcher 物件,該物件包含該Servlet的資源。
        getNamedDispatcher(String name):返回給定命名的servlet的RequestDispatcher 物件。
        getResource(String path):返回特定路徑的URL物件。
        getResourceAsStream(String path):返回給定path的資源,將其作為輸入流
        getResourcePaths(String path):返回web應用中所有資源路徑的列表(類似於目錄),它們的完整子路徑應匹配提供的路徑引數。
    */
    public String getRealPath(String path); 
    public RequestDispatcher getRequestDispatcher(String path);
    public RequestDispatcher getNamedDispatcher(String name);
    public URL getResource(String path) throws MalformedURLException;
    public InputStream getResourceAsStream(String path);
    public Set<String> getResourcePaths(String path);

    /**2.ServletContext。
        返回特定URL的ServletContext物件。
    */
    public ServletContext getContext(String uripath);

    /**3.attribute屬性的操作。
        getAttribute(String name):返回包含的指定name的屬性值
        setAttribute(String name, Object object):
        removeAttribute(String name):從servlet context中移除給定的屬性。
        getAttributeNames():返回servlet context中的包含的可用的attribute names。
    */
    public Object getAttribute(String name); 
    public void setAttribute(String name, Object object);   
    public void removeAttribute(String name);    
    public Enumeration<String> getAttributeNames();        
    /**4.Servlet資訊相關。
        getServerInfo():返回servlet執行的容器的servlet的名稱和版本。
        getServletContextName():返回web應用的名稱。
    */

    public String getServerInfo();      
    public String getServletContextName(); 
    /**5.引數相關。
        getInitParameter(String name):返回ServletContext指定的初始化引數。
        getInitParameterNames():返回context初始化引數的名稱集合。
    */
    public String getInitParameter(String name);
    public Enumeration<String> getInitParameterNames(); 
    public boolean setInitParameter(String name, String value);

    /**6.log相關。
        向log file寫日誌
    */
    public void log(String msg);
    public void log(String message, Throwable throwable);       
}

5.HttpServlet


public abstract class HttpServlet extends GenericServlet{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    public HttpServlet() { }


    /*1.Servlet容器呼叫此service(ServletRequest req, ServletResponse res)方法。*/
    public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException{
        HttpServletRequest  request;
        HttpServletResponse response;
        /**java中的instanceof運算子是用來在執行時指出物件是否是特定類的一個例項
           此處用來校驗req和res是否為Http型別的引數*/      
        if (!(req instanceof HttpServletRequest &&  res instanceof HttpServletResponse)) {            
            throw new ServletException("non-HTTP request or response");
        }
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
        /**跳轉至service(HttpServletRequest req, HttpServletResponse resp)函式。*/
        service(request, response);
    }

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        //1.獲取請求的型別
        String method = req.getMethod();
        //如果是Get方法
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            //lastModified值為-1,無快取,實時重新整理。HttpServlet的預設值為-1.
            if (lastModified == -1) {
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    //如果快取時間>=伺服器檔案修改時間,則直接返回304
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);           
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);           
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);           
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);           
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);          
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    /**
        設定Last-Modified實體頭欄位(如果尚未設定並且該值有意義)。在doGet之前呼叫,
        以確保在寫入響應資料之前設定頭。一個子類可能已經設定了這個頭,所以我們檢查。      
    */
    private void maybeSetLastModified(HttpServletResponse resp,long lastModified) {                             
        if (resp.containsHeader(HEADER_LASTMOD))
            return;
        if (lastModified >= 0)
            resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }     

    //HttpServlet預設返回-1,無快取機制。
    protected long getLastModified(HttpServletRequest req) {
        return -1;
    }

    /**doGet()與doPost()方法:*/
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }   
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
}

6.request請求中的If-Modified-Since標籤與response響應中的Last-Modified標籤。

參考 http://www.cnblogs.com/zh2000g/archive/2010/03/22/1692002.html

伺服器會把這個時間與伺服器上實際檔案的最後修改時間進行比較。如果時間一致,那麼返回HTTP狀態碼304(不返回檔案內容),
客戶端接到之後,就直接把本地快取檔案顯示到瀏覽器中。如果時間不一致,就返回HTTP狀態碼200和新的檔案內容,客戶端接到之後,
會丟棄舊檔案,把新檔案快取起來,並顯示到瀏覽器中。
If-Modified-Since,Http請求訊息頭標籤:
在傳送HTTP請求時,把瀏覽器端快取頁面的最後快取時間。
一起發到伺服器去,在客戶端的快取儲存中有伺服器發過來的該檔案的最後伺服器修改時間。

lastModified,Http響應訊息頭標籤:
在瀏覽器第一次請求某一個URL時,伺服器端的返回狀態會是200,內容是客戶端請求的資源,
同時有一個Last-Modified的屬性標記此檔案在伺服器端最後被修改的時間。

比較If-Modified-Since引數和lastModified 的新舊,決定是否重新整理瀏覽器快取,如果lastModified 使用預設的-1L,總是重新整理。

在HttpServlet類中定義了一個getLastModified方法,語法定義如下: protected long getLastModified(HttpServletRequest req)
HttpServlet類中定義的getLastModified方法總是返回一個負數,
在HttpServlet子類中可以對這個方法進行覆蓋,返回一個代表當前輸出的響應內容的修改時間,HttpServlet類的
service方法可以根據這個返回值在響應訊息中自動生成Last-Modified頭欄位。
一般情況下,瀏覽器都會快取已經訪問過的頁面內容,getLastModified方法的返回值可以影響瀏覽器如何處理和利用快取內容。

GET:它本質就是傳送一個請求來取得伺服器上的某一資源.
資源通過一組HTTP頭和呈現資料(如HTML文字,或者圖片或者視訊等)返回給客戶端。

HEAD:只請求頁面的首部。
HEAD和GET本質是一樣的,區別在於HEAD不含有呈現資料,而僅僅是HTTP頭資訊。

POST:向伺服器提交資料。這個方法用途廣泛,幾乎目前所有的提交操作都是靠這個完成。
PUT:這個方法比較少見。HTML表單也不支援這個。本質上來講, PUT和POST極為相似,都是向伺服器傳送資料,
但它們之間有一個重要區別,PUT通常指定了資源的存放位置,而POST則沒有,
POST的資料存放位置由伺服器自己決定。
DELETE:請求伺服器刪除指定的資源。
OPTIONS:允許客戶端檢視伺服器的效能。
TRACE: 請求伺服器在響應中的實體主體部分返回所得到的內容。

相關文章