http://dev2dev.bea.com.cn/techdoc/wlportal/20031027.html
簡介
頁面流提供了一種方便構建Web應用業務和導航邏輯的編程模型。為了顯示和修改頁面流動作所收集的數據,WebLogic Workshop 8.1 數據綁定框架將用戶界面中的NetUI JSP標簽綁定到Web應用數據上。兩者相互結合為構建基于Web的用戶界面提供了一種令人矚目的開發模型。
數據綁定是一個重要主題;這里我們將介紹用于綁定、顯示、更新數據的表達式語言和JSP標簽;并將演示如何創建一個簡單的表單。本文假定讀者對基本頁面流和JSP程序設計較為熟悉。文中用到的所有代碼都可以從附帶的下載文件
Web application (1.4 MB)中獲得。
參與者 綁定UI到數據的過程需要三個參與者--業務對象/數據、NetUI JSP標簽庫、表達式語言。業務對象中含有需要綁定到JSP頁面的數據。JSP標簽以特定視圖(只讀或可更新)的方式在Web瀏覽器中繪制數據,而表達式語言則將兩者粘合在一起,從而可以通過JSP標簽引用業務對象的屬性。借助三個參與者的實例,Workshop開發人員可以用靈活且優雅的方式創建用于顯示、更新、及創建數據的用戶界面。
整篇文章我們都會用到一個定義了字段和屬性的JavaBean,屬性更適合說明數據是如何綁定到不同類型之上的。該表單的一部分如下;其中定義了一個公共屬性name 和一個公共字段eMail。可以把該類看作一個針對簡單Web頁面的業務對象,該Web頁面演示了如何綁定到表單的屬性。
public class Customer { public String eMail = ""; private String name;
public String getName() { return this.name; } . . . } [binding/Controller.jpf] |
URL 也是該bean的一個屬性。除此之外,同一頁面流中還定義了一個LocationInfo 類,它暴露了可以進行數據綁定的復雜類型。
為了更快將讀者引進門,我們假定代表用戶"Dave Smith"的Customer對象已經存在,并且可以通過String類型的關鍵字"customer"在request的屬性映射中進行訪問。這個Customer的姓名可以借助JSP的NetUI標簽顯示在Web頁中。
Customer Name: <netui:label value="{request.customer.name}"/> [binding/simple.jsp] |
它在Workshop JSP 設計器中顯示如下:
Customer Name: {request.customer.name} |
并生成如下的HTML輸出:
Customer Name: <span>John Smith</span> |
下面我們將從整體上更進一步分析這個例子和表達式。
表達式語言
表達式語言使得Workshop開發人員能夠使用一種簡單的語法在JSP頁面中引用業務對象的數據。通常,一個表達式引用業務對象上的一個或一系列屬性,從而可以唯一標識JSP標簽所綁定的數據。在以上例子中,表達式{request.customer.name}引用Customer JavaBean中的name 屬性,保存在request中的該JavaBean被稱作"customer"。
在JSP標簽屬性中,表達式分別以'{' 和'}'作為開始和結束標記。標簽探測一個屬性是否是表達式、或者其中是否包含表達式,從而對屬性進行求值。
一個表達式含有兩個獨立的部分--數據綁定上下文:在其中進行表達式求值;屬性:它是表達式求值返回的結果。在表達式{request.customer.name}中, request是綁定上下文,它引用JSP頁面request的屬性映射。表達式剩下的部分用來引用唯一的對象屬性。"customer"標識符用來在request的屬性映射中查找相關對象,name標識符用來獲取Customer 對象上的JavaBean 屬性name。從功能上講,該表達式等同于以下語句:
String name = ((Customer)request.getAttribute("customer")).getName(); |
同樣是引用相同的數據,表達式語法更為簡單。
除了request 上下文,還可以在其它數據綁定上下文中求值表達式,這些上下文用來訪問Web應用和頁面流環境的不同部分。綁定上下文還可以引用其它資源,比如通過url 或 bundle 上下文,它可以分別引用URL參數或資源包中的國際化字符串。完整的上下文及其引用對象/資源的列表如下:
上下文名稱 |
上下文引用的對象 |
actionForm |
和當前NetUI 表單標簽相關的動作表單 |
pageFlow |
某個用戶的當前頁面流 |
globalApp |
webapp中某個用戶的Global.app |
bundle |
用于引用聲明或隱含的資源包屬性 |
pageContext |
JSP 頁面的PageContext屬性映射 |
request |
request的屬性映射 |
session |
session的屬性映射 |
application |
servlet上下文的屬性映射 |
url |
用于當前請求的URL 查詢參數 |
container |
當使用重復標簽繪制某個數據集時,復雜數據綁定標簽用它來訪問當前條目 |
現在進一步討論上表中提到的一些上下文。
同樣是使用表達式,仍然存在多種不同方法來引用同一對象的同一屬性。例如,在我們的例子中,以下幾個表達式都是等價的:
- request["customer"]["name"] - request.customer.name - request["customer"].name - request.customer["name"] [binding/index.jsp] |
而且,表達式既可以訪問字段,也可以訪問屬性,這些屬性返回任意基本類型或Java Object類型,也可以返回它們的數組、列表和映射類型。一般說來,屬性命名遵循JavaBean屬性的setter和getter方法命名規范。關于暴露、命名、引用數據綁定對象屬性的特殊規則將在下面進行討論。
注意:對象的值既可以是只讀的也可以是可讀/寫的;對象對應的值的暴露方式決定了它對于表達式僅是只讀的,還是當Web瀏覽器向服務器提交數據時該值是可更新的。
訪問屬性
為了識別類暴露的可綁定數據屬性,表達式語言采用了JavaBean命名規范。符合以下條件的方法可以暴露一個只讀屬性:
- public 訪問權限 - 返回非void類型 - 無參數 - 以"get"作為名稱的開頭 |
可以使用表達式來引用數據綁定上下文中符合以上條件的任意方法。JavaBean屬性的名稱被包含在表達式中。例如,類中定義了一個屬性:
public String getName(); [binding/index.jsp] [binding/Controller.jpf] |
在表達式中它被引用為:
{request.customer.name} [binding/index.jsp] [binding/index.jsp] |
前提是一個Customer 對象被指定為“customer”并可以從request中得到。
在本例中,方法 getName 對應名為name的JavaBean屬性;這是通過去掉“get”并將第一個字符變成小寫之后得到的。一個例外是如果“get”之后的頭兩個字符都是大寫的,JavaBean屬性的名稱將在表達式中保持不變。例如,屬性:
public String getURL(); [binding/index.jsp] [binding/Controller.jpf] |
在表達式中被引用為:
{request.customer.URL} [binding/index.jsp] [binding/index.jsp] |
迄今為止,以上的name 和URL 屬性都是只讀的,因為我們沒有為它們定義setter方法,該方法可以用來更新對象的屬性值。一個JavaBean的setter方法需要符合以下條件:
- 以"set"作為名稱開頭 - 返回void類型 - 只有一個參數,它的類型與對應getter方法的返回類型相同 |
前面兩個屬性的JavaBean setter如下:
public void setName(String name); public void setURL(String url); [binding/index.jsp] [binding/Controller.jpf]
|
如果對象中同時存在正確定義的getter 和setter,屬性就是可讀/寫的。這種類型的屬性十分重要,因為可以從Web頁面更新它們的值。
訪問字段
可以通過表達式訪問公共字段。上面Customer類中定義的“eMail”字段就是一個例子。公共字段總是可讀/寫的。Customer的email地址在Web頁中可以顯示為:
<netui:label value="{request.customer.eMail}"/> [binding/index.jsp] [binding/Controller.jpf]
|
訪問數組或列表中的元素
使用常用的[index] 語法可以引用數組或列表中的條目。如果一個屬性被暴露為:
public String[] getZipArray(); [binding/Controller.jpf] [binding/index.jsp]
|
那么可以通過以下表達式訪問數組的第四個條目:
{request.locationInfo.zipArray[3]} [binding/index.jsp] [binding/index.jsp] |
假定LocationInfo對象可以在request中通過關鍵字“locationInfo”進行訪問,
如果一個屬性被暴露為:
public List getZipList(); [binding/index.jsp] [binding/Controller.jpf] |
那么以下表達式將會訪問列表中的第四個條目:
{request.locationInfo.zipList[3]} [binding/index.jsp] [binding/index.jsp] |
此類表達式求值還是略有不同。如果該表達式在一個長度僅為2的String[]上被求值,因為數組的長度小于表達式的引用長度,所以將會發生錯誤。如果屬性是列表類型,表達式將被簡單地求值為null。
使用NetUI Repeater標簽集可以方便地繪制數組和列表。稍候一篇dev2dev文章將會涉及該標簽集以及“container”綁定上下文。
訪問映射中的元素
表達式還可以采用與訪問屬性相同的語法來引用映射中特定鍵值對應的條目。例如,如果LocationInfo 類暴露了一個含有州名縮寫和州名的Map,并將其定義為帶有JavaBean getter方法的可綁定數據屬性:
public Map getAbbrevMap(); [binding/index.jsp] [binding/Controller.jpf] |
那么,以下兩個表達式都可以使用鍵值“CO”在映射中進行查找:
{request.locationInfo.abbrevMap.CO} {request.locationInfo.abbrevMap["CO"]} [binding/index.jsp] [binding/index.jsp]
|
訪問基本類型
采用以上任何機制暴露的數據類型可以是Java對象或基本類型。如果某個屬性或字段的類型是基本類型,比如int 或 boolean ,表達式的求值結果將是基本類型的Java對象包裝器。在本例中,將分別是Integer 和 Boolean。
表達式和NetUI JSP標簽
在NetUI JSP標簽上使用表達式、屬性可以引用業務對象屬性,并將數據顯示在Web頁面上。同一屬性的顯示方式取決于標簽的選擇。比如,綁定到NetUI label的字符串屬性是只讀的,而綁定到NetUI form內NetUI text box的字符串屬性卻是可讀/寫的。
目前存在三個NetUI標簽庫——HTML、復雜數據綁定、模板,本文不涉及后面兩個。HTML標簽集盡可能遵循HTML4.01規范并將自己的標簽和HTML中定義的元素相互結合。例如,textBox、 checkBox、 和radioButton 標簽分別顯示HTML輸入標簽類型“text”、 “checkbox”、 和“radio”。可以在Workshop標簽面板中找到這些標簽。

也可以在Workshop 8.1的Insert菜單中找到這些標簽。
NetUI HTML 標簽進一步可以分為兩類——只能顯示數據的標簽(或稱為只讀的)、能夠在服務器和Web瀏覽器之間“往返”的標簽(或稱為可讀/寫的)。可以使用諸如netui:form 內的textBox這類標簽在服務器和客戶端之間傳遞數據。關于此類標簽如何綁定數據,彼此之間也存在不同。
只讀NetUI標簽
只讀標簽使用表達式綁定屬性“value”來指定在頁面中繪制的數據。此類標簽只是簡單地通過表達式從業務對象中讀取數值并在Web頁面上繪制,可能還會對輸出結果進行格式處理。我們已經知道netui:label 標簽可以在頁面上繪制表達式的值,其實它也可以繪制普通文本:
<netui:label value="Blue"/> [binding/index.jsp] [binding/index.jsp]
|
因為label標簽不是到服務器的可往返標簽,所以它的value屬性可以是文本和表達式的組合。例如,想要顯示用反斜杠分開的客戶姓名和年齡,可以這樣使用label:
<netui:label value="{request.customer.age} / {request.customer.age}"/> [binding/index.jsp] [customerEntry/display.jsp]
|
每個表達式都將被求值,文本“/”將會混合到表達式結果中。
可讀/寫NetUI標簽
第二組標簽使用“dataSource”屬性,它們比較特殊因為被“dataSource”屬性引用的數據可以在Web瀏覽器和服務器之間往返。盡管dataSource屬性可以綁定表達式,但該屬性的value必須是一個純粹的表達式。與label標簽的value屬性不同,dataSource屬性不能混合文本和表達式,因為dataSource屬性的value被用在兩個地方:
- 繪制JSP頁面的時候通過表達式讀取值 - 當表單所在頁面POST的時候,通過同一個表達式將值寫回到業務對象中去。
|
采取這種方式,諸如textBox這樣的標簽就可以在服務器和Web瀏覽器之間來回傳輸數據。
如果在dataSource 屬性中混合使用文本,屬性就不是一個純粹的表達式并將報錯。因為dataSource 屬性通常被用于同數據庫交換數據,所以這里使用的表達式必須引用那些可以更新的數據綁定上下文,它們是actionForm、pageFlow、和 globalApp。 除此以外,所有其它綁定上下文在數據被POST到服務器時都是只讀的。
一個簡單例子
讓我們對前面的Customer例子稍作修改并利用NetUI編寫一個“Hello World”,從而形成一個實用的例子。本例中含有一個頁面流simpleForm 和一個JSP。JSP定義了一個綁定到頁面流中某個action的NetUI表單。當表單從瀏覽器中POST的時候,動作表單將被創建,提交的數據將被添加到動作表單中。接著,輸入頁面被刷新,剛剛在只讀label中顯示的信息將會顯示到可編輯的文本框中。這里所說的頁面流是指本文示例應用中的simpleForm。
首先我們來看看動作表單,它們負責封裝那些在頁面中被顯示并更新的數據。該類定義了通過NetUI HTML在JSP表單標簽內可編輯的屬性;它暴露了唯一一個屬性——message。
public static class MessageForm extends FormData { private String message;
public void setMessage(String message) { this.message = message; }
public String getMessage() { return this.message; } } [binding/index.jsp] [simpleForm/Controller.jpf]
|
接著我們來看看JSP頁面。這里的netui:form 標簽用于引用在表單POST時將會執行的頁面流動作。如果您對頁面流不熟悉,請參閱參考材料部分的頁面流參考手冊。一般來說,從可讀/寫NetUI標簽POST到服務器的更新必須在NetUI form 標簽中進行。
<netui:form action="echoMessage" focus=""> <table> <tr> <td>Current message:</td> <td><netui:label value="{actionForm.message}"/></td> </tr> <tr><td colspan="2"> </td></tr> <tr> <td><netui:label value="Message:"/></td> <td><netui:textBox dataSource="{actionForm.message}"/></td> </tr> </table> <netui:button value="echoMessage"/> </netui:form> [binding/index.jsp] [simpleForm/index.jsp]
|
JSP中有兩個附加NetUI標簽;其中netui:textBox 利用表達式{actionForm.message}在當前動作表單中顯示message的值。最初該值以及textBox都是空的。還有一個button標簽用來把表單提交到服務器。運行例子的時候,請在文本框中輸入“Hello World”并按下“Submit”。
看!當表單提交到服務器時,頁面流生命周期創建了接收POST數據(即本例中的message)的動作表單。因為textBox的dataSource 屬性對應的表達式被映射為HTML輸入標簽——{actionForm.message},所以message的值被路由到該屬性上。
一旦表單填充完畢,postback 動作就會執行并在頁面中重新繪制相同的表單。運行結果是:表達式{actionForm.message} 現在同時指向label和textBox 中顯示的“Hello World”。在文本框中輸入文本并重新提交表單可以修改該值。
技巧和經驗
如何訪問頁面流的成員屬性?
頁面流可以像其它類一樣暴露JavaBean屬性;利用表達式和pageFlow 綁定上下文可以綁定這些屬性。例如,如果一個頁面流用以下方法暴露屬性productName:
public String getProductName() { return productName; } [binding/index.jsp] [howdoi/Controller.jpf] |
就可以通過以下表達式在JSP中進行數據綁定:
{pageFlow.productName} [binding/index.jsp] [howdoi/index.jsp] |
你還可以綁定到頁面流暴露的公共字段。
應該在頁面流上暴露映射嗎?
在任何可以接受POST數據的數據綁定上下文中暴露Map類型都是危險的。問題在于映射可以變得很大,這就在服務上開了一個安全漏洞:因為網頁可以直接POST數據到Map,這將會導致map無限變大、消耗掉可觀的服務器資源。
通過類似request這樣的只讀數據綁定上下文來暴露Map類型要安全很多。上面的例子中,LocationInfo對象通過abbrevMap 屬性實現了這點。
如果在表單上暴露一個int 類型的屬性,從頁面POST出去的是什么類型?
當JSP頁面POST數據到服務器時,所有的數據都以字符串形式到達。應用POST數據到頁面流屬性或動作表單的過程中,NetUI會設法把已經POST到底層屬性或字段的字符串數據轉換為原有的類型。
例如,如果動作表單暴露一個類型為int、名稱為 age 的屬性,并且該字段可以從NetUI表單更新,類型轉換工作即是把String 類型的“42”的轉換成int 類型的42。如果轉換失敗,失敗信息將會顯示在運行WebLogic的命令提示窗口中。
如何顯示一個數據列表或數組?
利用NetUI復雜數據綁定標簽可以繪制諸如列表、數組、映射、和行集這類的數據集。這些將在接下來的dev2dev文章中討論。
表達式求值失敗會怎樣?
當JSP準備繪制時,如果一個表達式求值失敗了,JSP頁面上將會顯示一個失敗信息。假定有屬性綁定的例子:
Customer Name: <netui:label value="{request.customer.name}"/> [binding/index.jsp] [binding/index.jsp] |
如果request的屬性映射中不包含名為“customer”的對象,那么綁定將會失敗,因為name屬性無法訪問null對象。請嘗試將例子改為:
Customer Name: [binding/index.jsp] [binding/index.jsp] |
這樣就可以知道表達式求值是如何失敗的了。
Workshop 8.1 IDE如何幫我進行數據綁定?
Workshop可以用多種方式幫助JSP作者進行數據綁定,其中最重要的一種是通過數據面板。使用數據面板可以在JSP頁面中創建能夠顯示或更新數據的NetUI標簽。
例如,在Workshop中打開JSP頁面howdoi/index.jsp;在頁面上,數據面板顯示如下:
您可以看見productName屬性在面板中是可用的。如果拖動它到JSP頁面中,以下代碼將被生成并將在頁面中顯示產品名稱:
<netui:label value="{pageFlow.productName}"></netui:label>
|
試著添加其它屬性到頁面流中并拖動它們到JSP頁面中,從而生成可以顯示數據的NetUI標簽。
如果頁面上有動作表單,它上面的屬性也可以被拖動到JSP上。
結論
本文覆蓋的范圍很廣。現在重新回顧一下,表達式語言使得NetUI JSP能夠引用業務對象暴露的數據、頁面流、以及數據綁定上下文暴露的其它對象和資源。表達式可以引用這些對象的屬性和字段,并且NetUI HTML標簽既可以用于顯示數據,也可以用于從Web瀏覽器更新數據。
本文附帶的應用程序包含多個示例,其中包括基本數據綁定規則的例子、Hello World例子、和一個創建、顯示、和編輯Customer對象的復雜例子。
在以后的文章中我們還會介紹更多的內容,希望本文使您對WebLogic Workshop 8.1的從UI到數據的綁定有了初步了解。