---第六章 Servlet

Servlet是Server + Applet的縮寫,表示一個服務器應用.Servlet其實就是一套規範,我們按照這套規範寫的代碼就可以直接在Java的服務器上面運行.如下圖所示的是Servlet3.1中Servlet的結構圖

一,Servlet接口

public interface Servlet {
 /*
      init方法在容器啓動時被容器調用(注:load-on-startup可以指定Servlet被創建的時機,若爲負數或者不設置,
    則在第一次請求時[即第一次調用該Servlet才被創建],若爲0或者正數,則在當前WEB應用被Servlet容器加載時創建實例,
    且數越小越早被創建),只會被調用一次;
*/
    public void init(ServletConfig config) throws ServletException;
    /*
     用於獲取ServletConfig,ServletConfig顧名思義指的是Servlet的配置,比如我們在web.xml中定義Servlet時通過init-param
    標籤配置的參數就是通過ServletConfig來保存的.
    */
    public ServletConfig getServletConfig();
   /*
      service方法用於具體處理一個請求
   */
   public void service(ServletRequest req, ServletResponse res) 
  throws ServletException, IOException;
   /*
    getServletInfo方法可以獲取一些Servlet相關的信息,若作者,版權等,這個方法需要自己實現,默認返回空字符串;
   */
   public String getServletInfo();   
    /*
      主要用於在Servlet銷燬(一般指關閉服務器)時釋放一些資源,也只會調用一次
    */    
   public void destroy(){}

二,ServletConfig


package javax.servlet;

import java.util.Enumeration;

 public interface ServletConfig {
    /*
   用於獲取Servlet的名字,也就是我們在web.xml中定義的servlet-name
    */
    public String getServletName();
  /*
   返回值ServletContext代表的是我們這個應用的本身,那麼ServletContext裏邊設置的參數就可以被當前應用的所有Servlet共享了[注:做項目時,參數可以保存在Session中,也可以在Application中,而後者很多時候就是保存在ServletContext中];
   我們可以這麼理解,ServletConfig是Servlet級的,而ServletContext是Context(也就是Application)級的,另外我們可以獲取更高一層的站點級也就是Tomcat中的Host級的相應操作:在Servlet的標準中有這麼一個方法:public ServletContext getContext(String uripath),它可以根據路徑獲取到同一站點下的別的應用的ServletContext!由於安全原因,一般返回null,如果想使用需要進行一些設置.
  */
    public ServletContext getServletContext();
   //用於獲取init-param配置的參數
    public String getInitParameter(String name);
//用於獲取配置的所有init-param的名字的集合
    public Enumeration<String> getInitParameterNames();

}

三 GenericServlet
GenericServlet是Servlet的默認實現,主要做了三件事:
①實現了ServletConfig接口,可以直接調用ServletConfig中的方法;
②提供了無參的init方法;
③提供了log方法;

  /*
   GenericServlet實現了ServletConfig接口,當需要調用ServletConfig中方法的時候,可以直接調用.比如獲取ServletContext的時候可以直接調用getServletContext,而無須調用getServletConfig().getServletContext(),不過底層實現其實是內部調用,源碼如下:
  */
   public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletContext();
    }
/*
 GenericServlet實現了Servlet的init(ServletConfig config)方法,在裏面將參數config設置給了內部變量config,然後調用了無參的init()方法;
 這種做法有三個作用:
 ①將參數config設置給了內部屬性config,這樣就可以在ServletConfig的接口方法中直接調用config相應的方法來執行;
 ②當我們在寫Servlet的時候就可以只處理自己的初始化邏輯,而不需要關心config了;
 ③在重寫init()方法時不需要在調用super.init(config)了,如果在自己的Servlet中重寫了帶參的init方法,一定記着調用super.init(config),否則會報空指針異常
*/
 //GenericServlet提供了兩個log方法,一個記錄日誌,一個記錄異常
    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.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);
    }

四 HttpServlet
HttpServlet是用HTTP協議實現的Servlet的基類,SpringMVC中的DispatchServlet就是繼承該類,HttpServlet主要重寫了service方法.在service方法中首先將ServletRequest和ServletResponse轉換爲了HttpServletRequest和HttpServletResponse ,然後根據HTTP請求的類型不同將請求路由到了不同的處理方法

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;

        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }

  protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {

                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    doGet()

                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    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);
        }
    }
    /*
    具體處理方法時doXXX的結構,.doGet,doPost,doPut和doDelete方法都是模板方法,而且子類如果沒有實現將拋出異常,在調用doGet方法之前還對是否過期做了檢查,如果沒有過期則直接返回304狀態碼使用緩存;doHead調用了doGet的請求,然後返回空body的Response;doOption和doTrace主要用來做一些調試工作,doOtion返回所有支持的處理類型的集合,正常情況下可以禁用,doTrace是用來遠程診斷服務器的,它會將接收到的header原封不動地返回
    */
 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);
        }
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章