java servlet

在Web伺服器端載入並運行的Java應用程式具體運行在Servlet引擎管理的JVM上。Servlet容器負責Servlet和用戶的通信以及調用Servlet的方法。Servlet和用戶的通信採用請求/回響模式。用於以動態回響客戶機請求形式擴展Web伺服器(Web Container)的功能。Servlet是開發伺服器端應用程式的一個很好選擇, Servlet與JSP結合使用,能提供更強大的伺服器端功能。 一個servlet就是Java程式語言中的一個類,它被用來擴展伺服器的性能,伺服器上駐留著可以通過“請求-回響”編程模型來訪問的應用程式。

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的服務。

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元素。

listeners.ContextListener

處理錯誤

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

exception.BookNotFoundException /errorpage.html exception.BooksNotFoundException/errorpage.html exception.OrderException /errorpage.html

共享信息

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

使用作用域對象

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

表14-3 作用域對象

作用域對象 哪些組件可以對其進行訪問
Web 上下文 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 書店作用域屬性

java servlet java servlet

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

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

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

· 多個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的do Method方法(在該方法中, 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頁包含了Add To Cart。 CatalogServlet 將命名為Add的參數提出,如下:

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

相關詞條

熱門詞條

聯絡我們