Java Servlet技術

來源:互聯網
上載者:User
文章目錄
  • 使用範圍對象
Java Servlet技術

Stephanie Bodoff

當Web剛開始被用來傳送服務時,服務提供者就已經意識到了動態內容的需要。Applet是為了實現這個目標的一種最早的嘗試,它主要關注使用用戶端平台來交付動態使用者體驗。與此同時,開發人員也在研究如何使用伺服器平台實現這個目標。開始的時候,公用網關介面(Common Gateway Interface ,CGI)指令碼是產生動態內容的主要技術。雖然使用得非常廣泛,但CGI指令碼技術有很多的缺陷,這包括平台相關性和缺乏可擴充性。為了避免這些局限性,Java Servlet技術因應而生,它能夠以一種可移植的方法來提供動態、面向使用者的內容。

什麼是 Servlet?

一個servlet就是Java程式設計語言中的一個類,它被用來擴充伺服器的效能,伺服器上駐留著可以通過“要求-回應”編程模型來訪問的應用程式。雖然servlet可以對任何類型的請求產生響應,但通常只用來擴充Web伺服器的應用程式。Java Servlet技術為這些應用程式定義了一個特定於HTTP的 servlet類。

javax.servlet和javax.servlet.http包為編寫servlet提供了介面和類。所有的servlet都必須實現Servlet介面,該介面定義了生命週期方法。

當實現一個通用的服務時,您可以使用或擴充由Java Servlet API提供的GenericServlet類。HttpServlet類提供了一些方法,諸如doGet和doPost,以用於處理特定於HTTP的服務。

本章主要講述如何編寫對HTTP請求產生響應的servlet。這裡假設您已經瞭解了一些HTTP協議的基礎知識。如果對這些協議不熟悉的話,您可以從HTTP概述中對HTTP協議有一個初步的瞭解。

Servlet 樣本

本章使用Duke's Bookstore應用程式來說明與servlet編程相關的任務。表14-1列出瞭解決每個書店功能的servlet。每個編程任務用一個或多個servlet來說明。例如,BookDetailsServlet說明如何處理HTTP GET請求,BookDetailsServlet和CatalogServlet顯示如何構建響應,而CatalogServlet 則說明如何跟蹤會話資訊。

141 Duke's Bookstore Servlet例子 

功能

Servlet

進入書店

BookStoreServlet

建立書店標識

BannerServlet

瀏覽書店的目錄

CatalogServlet

將書放入購物車

CatalogServlet,

BookDetailsServlet

擷取關於特定的某本書的一些詳細資料

BookDetailsServlet

顯示購物車

ShowCartServlet

從購物車中移除一本或多本書

ShowCartServlet

購買購物車中的書

CashierServlet

獲得對購買的確認

ReceiptServlet

這些書店應用程式的資料儲存在資料庫中,並通過協助類database.BookDB進行儲存。database包也包括BookDetails類,一個BookDetails類用來代表一種書。購物車和購物車項用cart.ShoppingCart 和cart.ShoppingCartItem來分別表示。

書店應用程式的原始碼放在<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1目錄中,這個目錄是在對指南包進行解壓縮時建立的。

要構建、安裝和運行這個執行個體,需完成以下步驟:

1.     在終端視窗中,轉到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1。

2.     運行build。 build目標將產生任何必要的編碼並且將檔案直接拷貝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1/build 。

3.     確認Tomcat已經開始執行。

4.     運行ant install 。install 目標式通知Tomcat 已經有了新的上下文 。

5.     啟動PointBase公司的資料庫伺服器,並且在沒完全準備好的情況下仍然指向資料庫(見從Web應用中訪問資料庫)。.

6.     開啟書店的URL http://localhost:8080/bookstore1/enter以運行該應用程式。

要部署該應用程式,要完成以下步驟:

1.     運行ant package。這個包任務是建立一個WAR 檔案,該檔案包含WEB-INF/classes中的應用程式類 和META-INF中的context.xml 檔案。

2.     確認Tomcat已經開始執行。

3.     運行ant deploy。Deploy目標將WAR拷貝到Tomcat,並且通知已經有了新的上下文。

故障排除   

一般的問題和其解決方案列舉了Web用戶端為什麼會失敗的一些原因。另外,Duke書店返回了以下異常:

·       BookNotFoundException——如果一本書不能在書店的資料庫中找到,則返回該異常。如果使用者沒有運行ant create-book-db來載入書店資料庫中的資料、或沒有運行資料庫伺服器、或資料庫已經崩潰,這些都將產生該異常。

·       BooksNotFoundException——如果書店的資料不能被擷取,則返回該異常。如果使用者沒有運行ant create-book-db來載入書店資料庫中的資料、或沒有運行資料庫伺服器、或資料庫已經崩潰,這些都將產生該異常。

·       UnavailableException——如果servlet不能擷取到用來表示書店的Web內容屬性,則返回該異常。如果您沒有拷貝指向(PointBase)的用戶端庫<PB_HOME>/lib/pbclient45.jar to <JWSDP_HOME>/common/lib、或者如果指向的(PiontBase)伺服器沒有運行、或使用者沒有定義Tomcat中用來引用指向資料庫(PointBase)的資料來源,這都將產生該異常。

因為指定了一個錯誤頁,使用者將看到這樣的一個訊息The application is unavailable. Please try later. 如果指定了一個正確頁,Web容器將產生一個包含A Servlet Exception Has Occurred訊息的預設頁和一個用來協助診斷異常產生原因的棧。如果使用errorpage.html,使用者可以瞭解Web容器決定異常產生原因的日誌。Web日誌位於<JWSDP_HOME>/logs目錄中,由jwsdp_log.<date>.txt來命名。

Servlet 的生命週期

一個servlet的生命週期由部署servlet的容器來控制。當一個請求映射到一個servlet時,該容器執行下列步驟。

1.     如果一個servlet的執行個體並不存在,Web容器

a.   載入servlet類。

b.   建立一個servlet類的執行個體。

c.   調用init初始化servlet執行個體。該初始化過程將在初始化servlet中講述。

2.     調用service方法,傳遞一個請求和響應對象。服務方法將在編寫服務方法中講述。

如果該容器要移除這個servlet,可調用servlet的destroy方法來結束該servlet。結束過程將在結束Serlvet中討論。

處理Servlet生命週期事件  

在servlet的生命週期中,使用者可以通過定義監聽器對象對事件進行檢測和產生反應。當生命週期事件發生時,調用該對象的方法。要使用這些監聽器對象,使用者必須定義監聽器類,並且指定相應的監聽器類。

定義監聽器類   

您可以將監聽器類定義為一個listener介面的實現。Servlet生命週期事件列出了可以檢測的事件和相應的必須實現的介面。當調用一個監聽器方法時,需向該方法傳遞一個包含事件適當資訊的事件。例如,向HttpSessionListener介面中的方法傳遞的是一個HttpSessionEvent事件,這個事件包含了一個HttpSession。

表14-2Servle生命週期事件

對象

事件

監聽器介面和事件類別

Web上下文
(見訪問Web上下文)

初始化和銷毀

javax.servlet.
ServletContextListener 和

ServletContextEvent

屬性的添加、刪除或替代

javax.servlet.
ServletContextAttributeListener 和

ServletContextAttributeEvent

會話
(見維護客戶給狀態)

建立、失效和逾時

javax.servlet.http.
HttpSessionListener 和

HttpSessionEvent

屬性的添加、刪除或替代

javax.servlet.http.
HttpSessionAttributeListener  和

HttpSessionBindingEvent

listeners.ContextListener類負責建立和移除在Duke書店應用程式中使用的資料庫助手和計數器對象。方法從ServletContextEvent中擷取Web內容物件,進而儲存(和移除)作為servlet內容屬性的對象。

import database.BookDB;
import javax.servlet.*;
import util.Counter;

  public final class ContextListener
  implements ServletContextListener {
  private ServletContext context = null;
 public void contextInitialized(ServletContextEvent event) {
    context = event.getServletContext();
    try {
      BookDB bookDB = new BookDB();
      context.setAttribute("bookDB", bookDB);
    } catch (Exception ex) {
      System.out.println(
        "Couldn't create database: "
        + ex.getMessage());
    }
    Counter counter = new Counter();
    context.setAttribute("hitCounter", counter);
    context.log("Created hitCounter"
      + counter.getCounter());
    counter = new Counter();
    context.setAttribute("orderCounter", counter);
    context.log("Created orderCounter"
      + counter.getCounter());
  }

    public void contextDestroyed(ServletContextEvent event) {
    context = event.getServletContext();
    BookDB bookDB = context.getAttribute(
      "bookDB");
    bookDB.remove();
    context.removeAttribute("bookDB");
    context.removeAttribute("hitCounter");
    context.removeAttribute("orderCounter");
  }
}

指定事件監聽器類    

為了指定一個事件監聽器類,使用者要為Web應用部署描述符添加一個listener元素。以下就是Duke書店應用程式的一個listener元素。

<listener>
  <listener-class>listeners.ContextListener</listener-class>
</listener>

處理錯誤  

當servlet執行時,可能產生許多異常。而當異常產生時,Web容器將產生一個包含A Servlet Exception Has Occurred訊息的預設頁。但是,使用者也可返回一個容器,該容器應包含為給定異常指定的錯誤頁。為了指定這樣一個頁,使用者要為Web應用添加部署描述符添加一個error-page元素。這些元素將Duke書店應用程式返回的異常映射到errorpage.html:

<error-page>
  <exception-type>
    exception.BookNotFoundException
  </exception-type>
  <location>/errorpage.html</location>
</error-page>
<error-page>
  <exception-type>
    exception.BooksNotFoundException
  </exception-type>
  <location>/errorpage.html</location>
</error-page>
<error-page>
  <exception-type>exception.OrderException</exception-type>
  <location>/errorpage.html</location> </error-page>

共用資訊 

像大多數對象一樣,Web組件通常與其他一些對象協同工作,以完成任務。要做到這一點,可以有多種方法。Web組件可以使用私人的helper(助手)對象(例如,JavaBeans組件),也可以共用那些有公用範圍屬性的對象,它們可以使用資料庫,還可以調用其他的Web資源。Java Servlet技術機制允許一個Web組件調用其他的Web資源,這在調用其他Web資源中有描述。

使用範圍對象

幾個協作的Web組件通過一些對象來共用資訊,這些對象是作為四個範圍對象的屬性來維護的。這些屬性可以通過表示域的類的[get|set]Attribute方法訪問。表14-3列出了這個範圍對象。

表14-3 範圍對象

範圍對象

哪些組件可以對其進行訪問

網路內容

javax.servlet.
ServletContext

Web上下文中的Web組件。見訪問Web上下文

會話

javax.servlet.
http.HttpSession

處理屬於會話的請求的Web組件。見維護用戶端狀態。

請求

javax.servlet.
ServletRequest

的子類型

處理請求的Web組件。

javax.servlet.
jsp.PageContext

建立對象的JSP頁。見隱式對象。

圖14-1顯示了Duke書店應用程式維護的範圍屬性。

圖14-1 Duke書店範圍屬性

控制對共用資源的並發訪問

在多線程的伺服器中,可能出現對共用資源的並發訪問。除了範圍對象屬性外,共用資源還包括儲存空間中的資料(如執行個體和類變數)、外部對象(如檔案)、資料庫連接和網路連接。並發訪問可出現在多個情況下。

·  多個Web組件訪問儲存在Web上下文中的對象。t

·  多個Web組件訪問儲存在會話中的對象。

·  一個Web組件中的多個線程訪問執行個體變數。一個Web容器一般為每個請求建立一個線程來處理。如果使用者確認一個servlet執行個體每次只處理一個請求,servlet就能實現SingleThreadModel 介面。如果servlet實現了這個介面,使用者就能確保servlet的服務方法中不可能有兩個線程並發執行。Web容器可通過同步訪問一個servlet的單獨執行個體、或者通過維護一個Web組件池為每個執行個體調用一個新的請求來實現。這個介面並不能防止Web組件訪問共用資源(如靜態類變數、外部對象)導致的同步問題

當資源可以並發訪問時,使用資源也就可以用不一致的方式。為了防止這樣的情況發生,使用者必須使用在Java指導中的線程單元中描述的同步機制來控制訪問。

在以前的部分中,我們說明了被多個servlet共用的5個範圍屬性: bookDB, cart, currency, hitCounter和orderCounter。bookDB屬性將在下一節中討論。cart, currency和counter可以被多線程的servlet設定和讀。使用同步方法來控制訪問以防止這些對象的使用不一致。例如,下面是一個util.Counter類:

public class Counter {
  private int counter;
  public Counter() {
    counter = 0;
  }
  public synchronized int getCounter() {
    return counter;   }
  public synchronized int setCounter(int c) {
    counter = c;     return counter;
  }
  public synchronized int incCounter() {
    return(++counter);
  }
}

訪問資料庫

在Web組件之間共用,並且在對一個Web應用被調用的間隙內維持的資料通常是由一個資料庫來維護的。Web組件使用JDBC 2.0 API來訪問關聯式資料庫。書店應用程式的資料由資料庫來維護,並通過助手類database.BookDB訪問。例如,當使用者購買書後,ReceiptServlet調用BookDB.buyBooks方法來更新書的清單。buyBooks方法為每本包含在購物車中的書調用buyBook。為了確保命令被完全執行,buyBook的調用程式將被封裝在一個單獨的JDBC交易處理中。通過[get|release]Connection方法可以使共用資料庫串連同步使用。

public void buyBooks(ShoppingCart cart) throws OrderException {
  Collection items = cart.getItems();  
 Iterator i = items.iterator();
  try {
    getConnection();
    con.setAutoCommit(false);
    while (i.hasNext()) {
      ShoppingCartItem sci = (ShoppingCartItem)i.next();
      BookDetails bd = (BookDetails)sci.getItem();
      String id = bd.getBookId();
      int quantity = sci.getQuantity();
      buyBook(id, quantity);
    }
    con.commit();
    con.setAutoCommit(true);
    releaseConnection();
  } catch (Exception ex) {
    try {
    con.rollback();
    releaseConnection();
    throw new OrderException("Transaction failed: " +
      ex.getMessage());
    } catch (SQLException sqx) {
      releaseConnection();
      throw new OrderException("Rollback failed: " +
        sqx.getMessage());
    }
  }
}

初始化 Servlet

在Web容器載入和執行個體化servlet類之後、servlet執行個體傳遞來自用戶端的請求之前,Web容器對servlet進行初始化。使用者可以自訂這個初始化過程,以允許servlet讀持久的配置資料、初始化資源,並且忽略Servlet介面的init方法以執行任何其它的一次性的活動。servlet必須使用UnavailableException來完成初始化過程。

所有的訪問書店資料庫的servlet(BookStoreServlet, CatalogServlet, BookDetailsServlet, 和 ShowCartServlet)在它們的init方法中初始化一個變數,指向用Web上下文監聽器建立的資料庫小幫手物件。

public class CatalogServlet extends HttpServlet {
 private BookDB bookDB;
 public void init() throws ServletException {
   bookDB = (BookDB)getServletContext().
     getAttribute("bookDB");
   if (bookDB == null) throw new
     UnavailableException("Couldn't get database.");
 }
}

編寫服務方法

servlet提供的服務實現在GenericServlet的service方法、HttpServlet的doMethod方法(在該方法中,Method可以帶Get、Delete、Options、Post、Put、Trace的值),或者是任何其他的由實現了Servlet介面的類定義的協議指定(protocol-specific)的方法中。在這一章剩下的部分中,服務方法這個術語將用於在一個向用戶端提供服務的servlet類中定義的任何方法。

服務方法的一般模式是從請求中提取資訊、訪問外部資源並且基於這些資訊填充響應。

對於HTTPservlet來說,填充響應的正確過程是:首先填充回應標頭,然後從響應中擷取一個輸出資料流,最後編寫輸出資料流的所有主體內容。回應標頭必須在PrintWriter或ServletOutputStream被擷取到之前設定好,因為HTTP協議希望獲得主體內容前的所有頭的資訊。下兩節將描述如何從請求中獲得資訊和產生響應。

從請求中獲得資訊

一個請求包含用戶端和servlet之間傳遞的資料。所有請求都實現了ServletRequest介面,該介面為訪問一下的資訊定義了方法:

·  參數,通常用來在用戶端和servlet之間傳送資訊

·  對象屬性(Object-valued attribute),通常用來在servlet容器與servlet之間或在協作的servlet之間傳遞資訊

·  有關協議的資訊,用來在請求、用戶端和涉及到該請求中的伺服器之間的通訊。

·  有關地區化的資訊。

例如,在CatalogServlet中,顧客希望購買的書的標識符作為參數包含在請求中。下面的這段代碼說明了如何使用getParameter方法提取標識符。

String bookId = request.getParameter("Add");
if (bookId != null) {
  BookDetails book = bookDB.getBookDetails(bookId);

使用者也可以從請求中擷取一個輸入資料流,並對資料進行手工解析。要讀字元資料,可以使用由請求的getReader方法返回的 BufferedReader對象來完成。而要讀位元據,可以使用getInputStream 返回的ServletInputStream。

HTTP serlvet通過HTTP請求對象傳遞,HttpServletRequest包含了請URL、HTTP頭、查詢字串等等。

一個HTTP請求URL包含以下幾部分:

http://[host]:[port][request path]?[query string]

請求路徑由以下元素組成:

·  上下文路徑:向前的斜線/和servlet的Web應用的上下文根的拼接。

·  servlet路徑:與啟用該請求的組件別名相應的路徑部分,由向前的斜線/開始。

·  路徑資訊:請求路徑的部分,不是上下文路徑或者servlet路徑的部分。

如果上下文路徑是/catalog和表14-4列舉出的別名,表14-5給出了一些執行個體,說明如何分解URL。

表14-4別名 

模式

Servlet

/lawn/*

LawnServlet

/*.jsp

JSPServlet

14-5 請求路徑元素

請求路徑

Servlet 路徑

路徑資訊

/catalog/lawn/index.html

/lawn

/index.html

/catalog/help/feedback.jsp

/help/feedback.jsp

null

查詢字串由參數和值的集合組成。每個參數都是從請求中用getParameter方法擷取得到的。這裡有兩種方法產生查詢字串:

·  一個查詢字串能在Web頁中明確地顯示出來。例如,一個HTML頁由CatalogServlet產生,該HTML頁包含了<a href="/bookstore1/catalog?Add=101">Add To Cart</a>。 CatalogServlet 將命名為Add的參數提出,如下:

         String bookId = request.getParameter("Add");

·  當一個表單與一個GET HTTP方法一起被提交時, 在URL上附加一個查詢字串。在Duke書店應用程式中,首先CashierServlet產生了一個表單,然後在表單中輸入一個使用者名稱,該表單附加在映射到ReceiptServlet的URL上,最後ReceiptServlet使用getParameter方法提取使用者名稱。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.