1. 程式人生 > >Java Web 學習(1) —— Servlet

Java Web 學習(1) —— Servlet

Java Web 學習(1) —— Servlet

 

一、 什麼是 Servlet

Java Servlet 技術是Java體系中用於開發 Web 應用的底層技術。
Servlet 是執行在 Servlet 容器(如 Tomcat)中的Java程式,而 Servlet 容器或 Servlet 引擎相當於一個 Web 伺服器,但是可以產生動態內容,而不僅是靜態資源。一個 Servlet 是一個 Java 程式,一個 Servlet 應用包含了一個或多個 Servlet,一個 JSP 頁面會被翻譯並編譯成一個 Servlet。
Servlet 應用執行在一個 Servlet 容器中,它無法獨立執行。Servlet 容器將來自使用者的請求傳遞給 Servlet 應用,並將 Servlet 應用的響應返回給使用者。

 

二、 Servlet API

Servlet API 有以下4個Java包:

  • javax.servlet:包含定義 Servlet 和 Servlet 容器之間契約的類和介面。主要包含:Servlet 介面,ServletRequest 介面,ServletResponse 介面,ServletContext 介面,ServletConfig 介面,RequestDispatcher 介面,Filter 介面,GenericServlet 類等。
  • javax.servlet.http:包含定義 HTTP Servlet 和 Servlet 容器之間契約的類和介面。
  • javax.servlet.annotation:包含用於 Servlet,filter,listener 的註解。它還為被註解元件定義元資料。
  • javax.servlet.descriptor:包含提供程式化登入 Web 應用程式配置資訊的型別。

Servlet 技術的核心是 Servlet,它是所有 Servlet 類必須直接或間接實現的一個介面。
Servlet 介面定義了Servlet 與 Servlet 容器之間的契約。這個契約歸結起來就是,Servlet 容器將 Servlet 類載入記憶體,並在 Servlet 例項上呼叫具體的方法。在一個應用程式中,每種 Servlet 型別只能有一個例項。

使用者請求致使 Servlet 容器呼叫 Servlet 的 service 方法,並傳入一個 ServletRequest 例項和一個 ServletResponse 例項。ServletRequest 中封裝了當前的HTTP請求,ServletResponse 表示當前使用者的HTTP響應。
對於每一個應用程式,Servlet 容器還會建立一個 ServletContext 例項。這個物件中封裝了上下文(應用程式)的環境詳情。每個上下文只有一個 ServletContext。每個 Servlet 例項也都有一個封裝 Servlet 配置的 ServletConfig。

Servlet 介面中定義了以下5個方法:

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();

3個 Servlet 生命週期方法:

  • init:當請求 Servlet 時,Servlet 容器會第一時間呼叫這個方法。這個方法在後續請求中不會再被呼叫。利用這個方法呼叫初始化程式碼。呼叫這個方法時,Servlet 容器會傳入一個 ServletConfig。
  • service:每當請求 Servlet 時,Servlet 容器就會呼叫這個方法。
  • destroy:當要銷燬 Servlet 時,Servlet 容器就會呼叫這個方法。當要解除安裝應用程式,或者當要關閉 Servlet 容器時,就會發生這種情況。一般會在這個方法中編寫清除程式碼。

2個非生命週期方法:

  • getServletlnfo:這個方法會返回 Servlet 的描述。
  • getServletConfig:這個方法會返回由 Servlet 容器傳給 init 方法的 ServletConfig。

 

三、 Hello Servlet

在 eclipse EE + Tomcat 環境下編寫一個簡單的 Servlet 例項

 

搭建環境

1. 下載 Tomcat 
下載完成後解壓壓縮包,參考資料夾下的`RUNNIG.txt`配置環境變數。
其中`"CATALINA_HOME"`是必選項,指向 Tomcat 的根目錄。
2. 將 eclipse 與 Tomcat 關聯
Window->Preferences->Server->Runtime Enviroments 點選 add 選擇下載的 Tomcat 版本,next 後可以選擇 Tomcat 的名字,路徑和 JRE 版本。Finish 完成建立。
Window->Show View->Other 可以選擇 Servers 視窗檢視 Tomcat,雙擊可以進一步配置 Tomcat,選擇埠號等等。

 

建立專案

1. 建立一個 Dynamic Web Project 在這個頁面下打勾,eclipse 會為我們自動建立一個 web.xml 檔案。

2. 右鍵 Project,配置 Build Path,點選 add Library,選擇 Server Runtime,匯入 Tomcat 中的 Servlet 類庫。

 

編寫 Servlet 類

編寫 Servlet 類有3種方式:

  1. 實現 Servlet 介面
  2.  繼承 GenericServlet 類 (implements Servlet, ServletConfig)
  3.  繼承 HttpServlet 類 (extends GenericServlet) (常用)

以實現 Servlet 介面為例,建立 HelloServlet 類

 1 package com.JL916;
 2 
 3 import java.io.IOException;
 4 import java.io.PrintWriter;
 5 
 6 import javax.servlet.Servlet;
 7 import javax.servlet.ServletConfig;
 8 import javax.servlet.ServletException;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 import javax.servlet.annotation.WebServlet;
12 
13 @WebServlet(name = "servlet", urlPatterns = {"/hi"})
14 public class HelloServlet implements Servlet {
15 
16   @Override
17   public void destroy() {}
18 
19   @Override
20   public ServletConfig getServletConfig() { return null; }
21 
22   @Override
23   public String getServletInfo() { return "hello"; }
24 
25   @Override
26   public void init(ServletConfig arg0) throws ServletException {}
27 
28   @Override
29   public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
30     arg1.setContentType("text/html");
31     PrintWriter writer = arg1.getWriter();
32     writer.print("<html><head></head><body>Hello Servlet</body></html>");
33   }
34 }

 

配置 Servlet

配置 Servlet 有兩種方式,一種是使用註解配置,即

@WebServlet(name = "servlet", urlPatterns = {"/hi"}) 

另一種是在 web.xml 檔案的 web-app 元素中新增 servlet 元素 和 servlet-mapping 元素,如下所示:

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.JL916.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hi</url-pattern>
</servlet-mapping>

注意:url樣式必須用一個'/'開頭

 

執行程式

在瀏覽器中輸入 http://localhost:8080(Tomcat 埠號)/專案名稱/(url-pattern)

 

四、 API 簡介

ServletRequest

對於每一個HTTP請求,Servlet容器都會建立一個 ServletRequest 例項,並將它傳給 Servlet 的 service 方法。ServletRequest 封裝了關於這個請求的資訊。
主要方法:

public int getContentLength();        // 返回請求主體的位元組數
public String getContentType();       // 返回請求主體的MIME型別
public String getParameter(String name);  // 返回指定請求引數的值 常用於返回表單的值或查詢字串的值
public String getProtocol();          // 返回請求的協議名稱和版本

 

ServletResponse

ServletResponse 介面表示一個 Servlet 響應。在呼叫 Servlet 的 service 方法前,Servlet 容器首先建立一個 ServletResponse,並將它作為第2個引數傳給 service 方法。

在 ServletResponse 中定義的方法之一是 getWriter 方法,它返回了一個可以向客戶端傳送文字的 java.io.PrintWriter。預設情況下,PrintWriter 物件使用 ISO-8859-1 編碼。
在傳送任何HTML標籤前,應該先呼叫 setContentType 方法,設定響應的內容型別,並將"text/html"作為一個引數傳入。

 

ServletConfig

當 Servlet 容器初始化 Servlet 時,Servlet 容器會給 Servlet 的 init 方法傳入一個 ServletConfig。
ServletConfig 封裝可以通過`@WebServlet`或者部署描述符傳給 Servlet 的配置資訊。這樣,傳入的每一條資訊就叫一個初始引數。一個初始引數有 key 和 value 兩個元件。
為了從 Servlet 內部獲取到初始引數的值,要在 Servlet 容器傳給 Servlet 的 init 方法的 ServletConfig 中呼叫 getlnitParameter 方法。

String getInitParameter(String name)
@WebServlet(name = "servlet", urlPatterns = {"/configtest"},
  initParams = {
    @WebInitParam(name = "admin", value = "JL916"),
    @WebInitParam(name = "email", value = "[email protected]")
  })

 

ServletContext

ServletContext 表示 Servlet 應用程式。每個 Web 應用程式只有一個上下文。在將一個應用程式同時部署到多個容器的分散式環境中時,每臺Java虛擬機器只有一個 ServletContext 物件。 通過在 ServletConfig 中呼叫 getServletContext 方法,可以獲得 ServletContext。
有了 ServletContext,就可以共享可以從應用程式中的所有資料處訪問到的資訊,並且可以動態註冊 Web 物件。前者將物件儲存在 ServletContext中的一個內部 Map 中。儲存在ServletContext 中的物件稱作屬性。
ServletContext中的下列方法負責處理屬性:

public Object getAttribute(String name);
public Enumeration<String> getAttributeNames();
public void setAttribute(String name, Object object);
public void removeAttribute(String name);

 

GenericServlet

GenericServlet 是一個抽象類,實現了 Servlet,ServletConfig,java.io.Serializable 介面,為 Servlet 介面中的除 service 外的方法提供預設實現。

// 將 config 物件儲存起來
public void init(ServletConfig config) throws ServletException {
  this.config = config;
  this.init();
}
// 子類可以覆蓋沒有引數的 init() 方法,ServletConfig 仍然由 GenericServlet 例項儲存
public void init() throws ServletException {

}

 

javax.servlet.http

javax.servlet.http 中的許多型別覆蓋了 javax.servlet 中的型別。
主要型別如下;

 
HttpServlet

HttpServlet 是一個抽象類,繼承自 GenericServlet。
HttpServlet 實現了 service 方法,將 ServletRequest 物件和 ServletResponse 物件轉換型別,再呼叫過載的service方法,獲取 request 的 method,再分別呼叫 doXXX 方法,因此無需再覆蓋 service 方法,只要覆蓋常用的 doGet 方法和 doPost 方法即可。

@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)) {
        // ...
        doGet(req, resp);
    } else if (method.equals(METHOD_HEAD)) {
        // ...
        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.

        // ...
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

 

HttpServletRequest 和 HttpServletResponse

HttpServletRequest 繼承自 ServletRequest,新增部分方法:

public String getMethod();      // 返回生成這個請求的HTTP方法名稱
public String getQueryString();   // 返回請求URL中的查詢字串
public HttpSession getSession();   // 返回與這個請求相關的會話物件 如果沒有 將建立一個
public Cookie[] getCookies();      // 返回Cookie物件陣列

HttpServletResponse 繼承自 ServletResponse,新增部分方法:

public void addCookie(Cookie cookie);                // 新增一個Cookie
public void sendRedirect(String location) throws IOException; // 傳送一條響應碼 將瀏覽器跳轉到指定位置

 

五、 部署描述檔案(web.xml)

使用部署描述符,如果需要修改配置值,如Servlet路徑,則不需要重新編譯Servlet類。
此外,可以將初始引數傳給一個Servlet,並且不需要重新編譯Servlet類,就可以對它們進行編輯。
部署描述符還允許覆蓋在 Servlet 標註中定義的值。Servlet 上的 @WebServlet 註解如果同時也在部署描述符中進行宣告,那麼它將不起作用。

 


參考資料:《Spring MVC 學習指南》 Paul Deck