在本文中,我們將緊隨本系列的第二期“ 有狀態網絡的 J2EE 技術”。在 那一期,您可以了解到 J2EE 中有狀態應用程序開發的一些基礎知識。這一次,我們將把 注意力從大范圍的概述轉移到如何創建和管理有狀態用戶體驗的細節上來。
創建有狀態應用程序的一個基本組成部分就是數據管理。通過從一個會話全面地收集有 關一個用戶的數據,我們可以為那個用戶創建一個簡要表(profile),然後使用這個簡要 表來個性化他的用戶體驗。為了智能地管理用戶數據,必須將其置於上下文中。出於這個 原因,我們使用 作用域。
在 J2EE 中有 4 種作用域(或者說上下文)用於管理數據:application(應用程序) 、session(會話)、request(請求)和page(頁面)。在本月,您將學到每種作用域為 J2EE 中的有狀態應用程序開發帶來了哪些東西,以及每種作用域最適合於哪種會話管理場 景。
首先我們來快速回顧一下 J2EE 中有狀態 Web 應用程序的開發。
構建有狀態 Web 應用程序
如果用戶只需要用 Web 應用程序來查看靜態內容,那麼除了 HTML 外我們不需要任何 其他的技術。但是,除了基於文本的信息和好看的背景顏色之外,大多數用戶還想從 Web 那裡得到更多的功能。實際上,目前多數的用戶訪問網絡都是為了執行復雜的電子商務交 易,參與在線社團,下載(和上傳)媒體,等等。對於大多數流行站點來說,交互性是關 鍵,而且,個性化的功能越多越好。對於這種類型的開發,我們使用一些高級的 Web 技術 ,例如 CGI、PHP、ASP、JSP 以及 Java Servlet。
交互性的一個重要的組成部分就是個性化。 個性化的 Web 站點能夠針對用戶的特定需 求和興趣來定制內容和輸出。個性化的一個重要組成部分就是 duration。用戶體驗不僅應 該具有內聚性,還應該加以擴展,超越單事務模式。例如,當登錄到像 Yahoo 這樣的一個 個性化的門戶時,您將建立一個 身份(identity),在 Yahoo 域中,無論導航到哪裡, 您的身份都不會改變。對於許多在線銀行、信用卡以及股票交易系統來說,也是如此。在 線商店也逐漸地采用了這種模型。
當我們談到擴展了單事務模式的用戶體驗時,我們稱之為 會話。當我們創建支持會話 而不是單獨的、孤立的事務的 Web 應用程序時,我們就是在創建 有狀態的應用程序。
在一個有狀態應用程序中,用戶可以導航到站點的許多不同的地方,並執行多個事務, 但是他將一直維護著他自己惟一的身份。這是可以實現的,因為系統會不斷地跟蹤他的會 話的 狀態。在 J2EE 中,這種類型的開發是通過將每個用戶的信息存儲在一個後端數據服 務器上來進行管理的。當一個用戶第一次登錄到站點時,他便建立一個惟一的 ID。從那一 刻開始,與該用戶的 ID 相關的數據便被存儲起來,並且在需要的時候系統可以訪問這些 數據。
至於 Java 平台,我們可以使用三種技術之一來將用戶 ID 與單個用戶關聯起來,這三 種技術是:URL 重寫、隱藏表單字段以及 HTTP cookies。在 J2EE 中,我們使用 HTTPSession API 來存儲、檢索會話數據,或者將會話數據與某一特定用戶 ID 相關聯。
除了存儲數據,我們還必須能夠 contextualize數據(將數據置於上下文中)。術語 作用域 指的是被存儲數據的上下文(即該數據的 “作用域”);對作用域的 適當處理在有狀態 Web 應用程序的設計中處於核心地位。
會話作用域
術語“作用域”指的是一個上下文,在這個上下文中數據被關聯或者存儲。 在傳統的獨立應用程序中存在著一些可以在其中關聯變量和對象引用的上下文(或作用域 )。典型的作用域包括:
局部/方法
類/對象/組件
包/庫
protected(受保護的)
全局/public(公共的)
在 Web 應用程序中所說的作用域不同於更傳統的、獨立的應用程序中所使用的作用域 。在 Web 應用程序中,作用域指的是一個對象可以多大程度地為一個應用程序的組件所使 用。而在獨立應用程序中,雖然作用域所指的也是可用性,但這裡的作用域是由代碼塊來 劃分界限的。
在 J2EE Web 應用程序中,一共有 4 種會話作用域:
page
request
session
application
每種 J2EE 作用域都有一個上下文,在這個上下文中可以關聯(存儲)基本類型的數據 和對象引用,以供享有同一上下文的其他組件使用。表 1 列出了這 4 種作用域,並說明 了它們是否能應用於 servlet 和 JSP 頁面,還給出了對每種作用域的描述。
表 1. 4種會話作用域
會話作用域 Servlets JSP 頁面 描述 page 否 是 代表與一個頁面相關的對象和屬性。一個頁面由一個編譯好的 Java servlet 類(可以帶有任何的 include 指令,但是沒有 include 動作)表示。這既包括 servlet 又包括被編譯成 servlet 的 JSP 頁面 request 是 是 代表與 Web 客戶機發出的一個請求相關的對象和屬性。一個請求可能跨 越多個頁面,涉及多個 Web 組件(由於 forward 指令和 include 動作的關系) session 是 是 代表與用於某個 Web 客戶機的一個用戶體驗相關的對象和屬性。一個 Web 會話可以也經常會跨越多個客戶機請求 application 是 是 代表與整個 Web 應用程序相關的對象和屬性。這實質上是跨越整個 Web 應用程序,包括多個頁面、請求和會話的一個全局作用域
接下來,我們將看看用於將數據存儲在這 4 種作用域中的一些機制。
將數據存儲在作用域中
在這 4 種作用域中,每一種作用域都有一個不同的機制,用於存儲並最終訪問上下文 相關的數據。每種作用域都有一個單獨的類,通過這個類可以存儲和檢索上下文相關的數 據。表 2 標出了 4 個類,分別對應於 4 種會話作用域。
表 2. 用於存儲有作用域的數據的類
類名 作用域 注釋 javax.servlet.jsp.PageContext page 特定於 JSP 的一個類型,代表當前的 JSP 頁面 。PageContext 是一個 抽象類,設計用來供 JSP 引擎供應商加以擴展以提供特定於實現的類型。 PageContext 實例提供了對所有與一個 JSP 頁面相關的名稱空間的訪問途徑 javax.servlet.http.HttpServletRequest requset 這種類型代表當前的 HTTP 請求。 HttpServletRequest 類型是一個接 口,設計用來供 servlet 引擎供應商加以實現。由客戶機提供的特定於請求的數據 (request方法,UTI,HTTP 參數,等等),以及由另一個 servlet 或者 JSP 頁面提供的 數據,都可以存儲在一個 request 作用域中 javax.servlet.http.HttpSession session 這種類型代表當前的 HTTP 會話。 HttpSession 類型是一個接口,設計 用來供 servlet 引擎供應商加以實現 javax.servlet.ServletContext application 這種類型代表整個的運行時 Web 模塊。 ServletContext 類型是一個模 塊,設計用來供 servlet 引擎供應商加以實現
乍一看來,使用這 4 種數據類型來存儲有作用域的數據是相當直觀的。然而,麻煩的 是,對於一個給定的場景,哪一種作用域最合適卻並不總是那麼清楚的事了。我們將看看 可能碰到的一些常見的場景,以便作出總結。另外,我們還將實際地討論,對於每一種環 境,哪一種作用域最適合。
作用域解決方案
在由 JSP 和 Java Servlet 規范定義的這 4 種作用域中,每一種作用域在 Web 應用 程序中都有其明確的用途。
page 提供代表一個 JSP 頁面的上下文,與用戶所看到的真實頁面之間常常具有一對一 的映射關系。這種作用域只能用於 JSP 頁面,並且也是所有對象,包括 JavaBean 組件的 默認作用域。具有 page 作用域的對象通常是那些在 scriptlet、表達式、JavaBean 標記 以及自定義標記中被訪問的局部變量。如果必須獲得一個有 page 作用域的對象的一個引 用,則可以在該頁面的 javax.servlet.jsp.PageContext 變量上調用 getAttribute() 。
request 最適合的環境是:單個的用戶請求可能涉及不止一個的 servlet 或 JSP 頁面 。request 是一種能夠在一個原子請求內跨越多個頁面的上下文。有 request 作用域的數 據存儲在 javax.servlet.ServletRequest 對象中(使用 javax.servlet.http.HttpServletRequest object )並通過使用 getAttribute() 和 setAttribute() 方法來訪問。
session 是有狀態 J2EE Web 應用程序作用域的核心和靈魂。正是這種作用域使得跨越 多個請求的持久用戶體驗的創建成為可能。 javax.servlet.http.HttpSession 是存儲有 session 作用域的數據的地方,可以通過調用 getAttribute() 和 setAttribute() 方法 來訪問。為了在一個 JSP 頁面內使用有 session 作用域的數據,必須首先聲明這個頁面 要參與會話。為了做到這一點,只需在頁面的任何地方(一般是在頂部)插入 JSP 會話屬 性 <%@ page session="true" %> 。
application 是具有最長運行時間的作用域。這是 J2EE 為全局數據提供的。應用程序 數據被一個應用程序模塊內的所有 Web 組件所共享。具有應用程序作用域的對象屬於 javax.servlet.ServletContext ,可以通過調用 getAttribute() 和 setAttribute() 方 法來訪問。
記住了這些定義,我們就可以制定使用不同作用域的一些原則:
對於 JSP 數據堅決使用 page 作用域。這是與 JSP 頁面打交道的最簡單的方式。JSP 頁面內所有數據的默認作用域都是 page,它允許您在為局部變量指定的范圍內(類/方法/ 局部變量作用域)使用這種數據。
清楚 JSP include 對作用域的影響。 page 作用域適用於單個的、經過編譯的 Java servlet 類。因為 include 指令是在編譯的時候處理的,包括在指令中的任何內容都是在 page 作用域的上行文中操作的。另一方面, include 動作是在運行的時候處理的。如果 使用了 include 動作,為了在兩個組件之間共享數據,應該使用 request 作用域。
為了在運行的時候在 Web 組件之間共享數據,使用 request 作用域。如果使用一個 forward 動作或者 include 動作來在兩個或更多組件之間共享一個請求,那麼通過將數據 的作用域設為 request 作用域便可以在這些組件之間共享該數據。
為了提供有狀態用戶體驗,使用 session 作用域。無論要構建的是在線商店,電子郵 件管理站點,個人化信息門戶,還是財務管理應用,session 作用域都是對用戶采取從請 求到請求的跟蹤或者為用戶提供無縫的、持久的環境的最佳選擇。
將 application 作用域專用於全局數據。應用程序對象是一些靜態的對象,為應用程 序內的所有對象所共享。對 application 作用域的使用應該保留給真正需要在組件之間共 享或者跨用戶會話的數據。典型的例子有:緩存的 DAO,JNDI 引用的緩存,或者任何類型 的公共工廠或者其他需要使用 Singleton 模式的組件。
結束語
對會話作用域(即在一個會話中存儲和維護的數據的上下文)的適當處理是有狀態應用 程序開發的關鍵。在本期的 J2EE 探索者中,我指出了 4 種 J2EE 會話作用域,以及用於 代表每種上下文的多個數據類型,並討論了一些實際的注意事項,這些注意事項將對您使 用作用域有所幫助。
下個月,我們將考察與 JSP 隱式對象(implicit object)相關的專題。隱式對象是指 預先定義好的對象,可以為所有 JSP 頁面所用。有些隱式對象提供了對本文中談到的 4 種作用域的訪問途徑。下個月,讓我繼續快樂的探索吧!