Servlet相關基礎知識總結

Servlet複習

  1. 什麼是Servlet?(簡介、工作流程、Servlet容器工作流程)

    • 簡介:

      • Servlet使用java編寫的服務器端應用程序,主要功能是在於交互式的瀏覽和修改數據,生成動態的Web內容
      • 運行在服務端,用來處理客戶端請求並響應給瀏覽器動態資源
      • 單實例,多線程
    • Servlet工作流程

      • 通過在web.xml中配置的url-pattern獲取到servlet-name
      • 之後根據servlet-name去尋找對應的Servlet類
      • 接着找到對應的Servlet類之後,執行對應的service方法,根據請求處理和響應
      • 返回響應信息
    • Servlet容器(tomcat)整體的工作流程

      img

  2. Servlet的核心API以及體系結構

    • 繼承體系圖:img

    • 核心API:

      • Servlet接口,定義了一系列的抽象方法.init(ServletConfig)、service(ServletReqeust,ServletResponse)、destroy()等是由容器調用的

        • init(ServletConfig):void
        • getServletConfig():ServletConfig
        • service(ServletReqeust,ServletResponse):void
        • getServletInfo():String
        • destroy():void
      • GenericServlet抽象類:

        • 實現了Servlet接口、ServletConfig接口、序列化Serializable接口,對Servlet接口提供通用實現,不與任何協議相關聯。
        • GenericServlet抽象類實現了Servlet接口中的init(ServletConfig)方法,其中有一個ServletConfig的private實例,容器會使用它來初始化init(ServletConfig),該方法中調用空的init()方法,重寫時需要注意添加super.init()
        • 實現GenericServlet類中全部方法。
      • HttpServlet抽象類:

        • HttpServlet類是GenericServlet類的子類。HttpServlet類爲Serlvet接口提供了與HTTP協議相關的通用實現,也就是說,HttpServlet對象適合運行在與客戶端採用HTTP協議通信的Servlet容器或者Web容器中。

        • 早期開發中,一般自定義Servlet類擴展字HttpServlet類

        • HttpServlet類實現了Servlet類接口中的Service(ServletRequest,ServletResponse)方法,實現方法中卻是調用的它的重載方法

          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) {
                          // servlet doesn't support if-modified-since, no reason
                          // to go through further expensive logic
                          doGet(req, resp);
                      } else {
                          long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                          if (ifModifiedSince < lastModified) {
                              // If the servlet mod time is later, call doGet()
                              // Round down to the nearest second for a proper compare
                              // A ifModifiedSince of -1 will always be less
                              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 {
                      //
                      // Note that this means NO servlet supports whatever
                      // method was requested, anywhere on this server.
                      //
          
                      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);
                  }
              }
              //重載service()方法中,首先調用HttpServletRequest類型的參數的getMethod()方法,獲得客戶端的請求方法,然後根據該請求方式來調用匹配的服務方法;如果爲GET方式,則調用doGet()方法,如果爲POST方式,則調用doPost()方法。
          
        • HttpServlet類爲所有的請求方式,提供了默認的實現doGet(),doPost(),doPut(),doDelete()方法;這些方法的默認實現都會向客戶端返回一個錯誤。

          
              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(405, msg);
                  } else {
                      resp.sendError(400, msg);
                  }
          
              }
          
              protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                  String protocol = req.getProtocol();
                  String msg = lStrings.getString("http.method_put_not_supported");
                  if (protocol.endsWith("1.1")) {
                      resp.sendError(405, msg);
                  } else {
                      resp.sendError(400, msg);
                  }
          
              }
          
          
        • 對於HttpServlet類的具體實現類,一般會針對客戶端的特定請求方法,覆蓋HttpServlet類中的相應的doXXX方法。

          	protected void doGet(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
          		doPost(request, response);
          	}
          
          	
          	
          	protected void doPost(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
                  //xxx
              }
          
      • ServletRequest接口:

        • 簡述:表示來自客戶端的請求,當Servlet容器接收到客戶端要求訪問特定Servlet的請求時,容器先解析客戶端的原始請求數據,把它包裝成一個ServletRequest對象

        • 方法:

          getContentLength() —— 返回請求正文的長度,如果請求正文的長度未知,則返回-1getContentType() —— 獲得請求正文的MIME類型,如果請求正文的類型爲止,則返回null;
          getInputStream() —— 返回用於讀取請求正文的輸入流;
          getLocalAddr() —— 返回服務端的IP地址;
          getLocalName() —— 返回服務端的主機名;
          getLocalPort() —— 返回服務端的端口號;
          getParameters() —— 根據給定的請求參數名,返回來自客戶請求中的匹配的請求參數值;
          getProtocal() —— 返回客戶端與服務器端通信所用的協議名稱及版本號;
          getReader() —— 返回用於讀取字符串形式的請求正文的BufferReader對象;
          getRemoteAddr() —— 返回客戶端的IP地址
          getRemoteHost() —— 返回客戶端的主機名
          getRemotePort() —— 返回客戶端的端口號
          
      • HttpServletRequest接口:

        • 簡述:ServletRequest接口的子接口,用於讀取HTTP請求中的相關信息。

        • 方法:

          getContextPath() —— 返回客戶端請求方法的Web應用的URL入口,如客戶端訪問的URL爲http://localhost:8080/hello/info,那麼該方法返回“/hello”;
          getCookies() —— 返回HTTP請求中的所有Cookie;
          getHeader(String name) —— 返回HTTP請求頭部的特定項;
          getHeaderName() —— 返回一個Enumeration對象,它包含了HTTP請求頭部的所有項目名;
          getMethod() —— 返回HTTP請求方式;
          getRequestURL() —— 返回HTTP請求的頭部的第一行中的URL;
          getQueryString() —— 返回HTTP請求中的查詢字符串,即URL中的“?”後面的內容;
          
      • ServletResponse接口:{ServletResponse中響應正文的默認MIME類型是text/plain,即純文本類型}

        • 簡述:Servlet通過ServletResponse對象來生成響應結果.

        • 方法:

          setCharacterEncoding() —— 設置相應正文的字符編碼。響應正文的默認字符編碼爲ISO-8859-1setContentLength() —— 設置響應正文的長度;
          setContentType() —— 設置響應正文的MIME類型;
          getCharacterEncoding() —— 獲得響應正文的字符編碼
          getContentType() —— 獲得響應正文的MIME類型
          setBufferSize() —— 設置用於存放響應正文數據的緩衝區的大小
          getBufferSize() —— 獲得用於存放響應正文數據的緩衝區的大小;
          reset() —— 清空緩衝區內的正文數據,並且清空響應狀態代碼及響應頭
          resetBuffer() —— 僅僅清空緩衝區的正文數據,不清空響應狀態代碼及響應頭;
          flushBuffer() —— 強制性地把緩衝區內的響應正文數據發送到客戶端;
          isCommitted() —— 返回一個boolean類型的值,如果爲true,表示緩衝區內的數據已經提交給客戶,即數據已經發送到客戶端;
          getOutputStream() —— 返回一個ServletOutputStream對象,Servlet用它來輸出二進制的正文數據;
          getWriter() —— 返回一個PrinterWriter對象,Servlet用它來輸出字符串形式的正文數據;
          
        • 其他:爲了提高數據傳輸速率,數據會先被寫入緩衝區中,當緩衝區中數據被提交到客戶端後,ServletResponse的isComitted方法返回true,以下幾種情況下,緩衝區內的數據會被提交給客戶,即數據被髮送到客戶端:

          1. 當緩衝區內的數據已滿時,ServletOutPutStream或PrintWriter會自動把緩衝區內的數據發送給客戶端,並且清空緩衝區;
          2. Servlet調用ServletResponse對象的flushBuffer方法;
          3. Servlet調用ServletOutputStream或PrintWriter對象的flush方法或close方法;
        • 注意:爲了確保SerlvetOutputStream或PrintWriter輸出的所有數據都會被提交給客戶,比較安全的做法是在所有數據都輸出完畢後,調用ServletOutputStream或PrintWriter的close()方法,tomcat會自動關閉

        • 調用ServletResponse對象的setContentType()和setCharacterEncoding()方法可以設置MIME類型和字符編碼,之後再調用ServletResponse的getOutputStream()或getWriter()方法,提交緩衝區內的正文數據;如此才使設置生效

      • HttpServletResponse接口:{HttpServletResponse中響應正文的默認MIME類型爲text/html,即HTML文檔類型}

        • 簡述:提供了與HTTP協議相關的一些方法,Servlet可通過這些方法來設置HTTP響應頭或向客戶端寫Cookie,其中也定義了一些代表HTTP響應狀態代碼的靜態常量

        • 方法:

          addHeader() —— 向HTTP響應頭中加入一項內容
          sendError() —— 向客戶端發送一個代表特定錯誤的HTTP響應狀態代碼
          setHeader() —— 設置HTTP響應頭中的一項內容,如果在響應頭中已經存在這項內容,則原來的設置被覆蓋
          setStatus() —— 設置HTTP響應的狀態代碼
          addCookie() —— 向HTTP響應中加入一個Cookie
          
      • ServletConfig接口:

        • 簡介:當Servlet容器初始化一個Servlet對象時,會爲這個Servlet對象創建一個ServletConfig對象,在Servlet對象中包含了Servlet的初始化參數信息。

        • 方法:

          getInitParameter(String name) —— 返回匹配的初始化參數值
          getInitParameterNames() —— 返回一個Enumeration對象,裏面包含了所有的初始化參數名
          getServletContext() —— 返回一個ServletContext對象
          getServletName() —— 返回Servlet的名字,即web.xml文件中相應<servlet>元素的<servlet-name>子元素的值;如果沒有爲servlet配置<servlet-name>子元素,則返回Servlet類的名字
          
      • ServletContext接口:

        • 簡介:Servlet與Servlet容器之間直接通信的接口,Servlet容器在啓動一個Web應用時,會爲它創建一個ServletContext對象。每個Web應用都有唯一的ServletContext對象,可以把ServletContext對象形象地理解爲Web應用的管理者,同一個Web應用中的所有Servlet對象都共享一個ServletContext,Servlet對象可以通過其訪問容器中的各種資源,如配置信息等。

        • 方法:

          setAttribute(String name, Object object) —— 把一個Java對象與一個屬性名綁定,並存入到ServletContext中;
          getAttribute() —— 返回指定數姓名的屬性值
          getAttributeNames() —— 返回一個Enumeration對象,包含所有存放在ServletContext中的屬性名
          removeAttributes() —— 從ServletContext中刪除匹配的屬性
          
          getContextPath() —— 返回當前Web應用的URL入口
          getInitParameter() —— 返回Web應用範圍內的匹配的初始化參數值。在web.xml中,直接在<web-app>根元素下定義的<context-param>元素表示應用範圍內的初始化參數
          getServletContextName() —— 返回Web應用的名字,即web.xml文件中<display-name>元素的值
          getRequestDispatcher() —— 返回一個用於向其他WEB組件轉發請求的RequestDispatcher對象
          
          getRealPath() —— 根據參數指定的虛擬路徑,返回文件系統中的一個真實的路徑
          getResources() —— 返回一個映射到參數指定的路徑的URL
          getResourceAsStream() —— 返回一個用於讀取參數指定的文件的輸入流
          getMimeType() —— 返回參數指定的文件MIME類型
          
          log(String msg) —— 向Servlet的日誌文件中寫日誌
          log(String message, Throwable throwable) —— 向Servlet的日誌文件中寫入錯誤日誌,以及異常的堆棧信息
          
          
    • Servlet類以及其相關類:

      • 體系圖:
        img
      • 與Servlet相關的類有三個,ServletConfig,ServletRequest和ServletResponse,它們都是通過容器傳遞給Servlet的;其中,ServletConfig是在Servlet初始化時傳給Servlet的,後兩個是在請求到達時調用Servlet傳遞過來的。
      • Request和Response流程:以TOMCAT爲例,
        • tomcat接到請求首先將會創建org.apache.coyote.Request和org.apache.coyote.Response,這兩個類是Tomcat內部使用的描述一次請求和相應的信息類,它們是一個輕量級的類。
        • 服務器接收到請求後,經過簡單解析將這個請求快速分配給後續線程去處理。
        • 之後當交給一個用戶線程去處理這個請求時又創建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response對象。
        • 這兩個對象一直貫穿整個Servlet容器直到要傳給Servlet,傳給Servlet的是Request和Response的Facade類。
        • img
  3. Servlet的生命週期?

    1. 會經過創建 、初始化、服務可用、服務不可用、處理請求、終止服務、銷燬等其中狀態
    2. 按照七種狀態生命週期可以分爲四個階段:
      1. 加載和實例化
        1. 在服務器運行中,客戶機首次向servlet發出請求時
        2. 重新裝入Servlet時(如服務器重新啓動、Servlet被修改)
        3. 在爲Servlet配置了load-on-startup時,服務器在啓動時自動裝入此Servlet
      2. 初始化
        1. Servlet實例化後,Servlet調用Servlet的init(ServletConfig config)方法對Servlet進行初始化。此過程可以讀取一些固定地數據、初始化JDBC的連接以及建立與其他資源的連接等操作。init()的方法參數ServletConfig對象由Servlet的容器創建並傳遞給Servlet,並在初始化完成後一直在內存中存在,直到Servlet被銷燬。
      3. 處理請求
        1. 服務器接收到客戶端的請求後,會爲該請求創建一個“請求”對象和一個“響應”對象並調用service()方法,service()方法會在調用其他方法處理請求。Servlet的生命週期中,service()方法可能被多次調用,當多個客戶端同時訪問某個Servlet的service()方法時,服務器會爲每個請求創建一個線程,這樣可以並行處理多個請求,減少處理的等待時間,提高響應速度,但是也會導致對同一對象的併發訪問問題。
      4. 銷燬
        1. 當Servlet容器需要終止Servlet(如web服務器即將被關掉或需要讓出資源),它會調用Servlet的destroy()方法使其釋放正在使用的資源
  4. 實現一個簡單的Servlet容器

    • 工作原理:用戶向服務器發送一個請求時,http://hostname:port/contextpath/servletpath,hostname和port用來與服務器建立TCP連接,後面的URL用來選擇在服務器中哪個子容器服務用戶的請求,映射工作由專門的一個類完成:org.apache.tomcat.util.http.mapper,這個類保存了tomcat的container容器中的所有子容器的信息。org.apache.catalina.connector.Request類在進入Container容器之前,Mapper將會根據這次請求的hostname和contextpath將host和context容器設置到Request的mappingData屬性中,所以當Request進入container容器之前,對於它要訪問哪個子容器就已經確定。

    • 採用MVC框架的實現中,其基本原理是將所有的請求都映射到一個Servlet,然後去實現servie方法,這個方法也就是MVC框架的入口。

    • 實現一個簡單的Servlet容器,源碼見:簡易tomcat

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