asp.net|js|程式 理想情況下,ASP.NET和JSF web頁面應該包含很少的代碼而且只是包含必要的HTML和標籤來產生頁面的組件,一個頁面的事件邏輯駐留於代碼檔案中。在ASP.NET中,每個web頁面與一個相應的子類化頁面ASP.NET類的.NET類檔案相關聯。有時,這些檔案被引用為"code-behind"檔案。在JSF中,每個web頁面都有一個相關聯的支援JavaBean類。ASP.NET的code-behind檔案和JSF支援bean都包含頁面屬性(例如標籤和輸入欄位)。JSF bean是用Java編寫的,而ASP.NET code-behind檔案可以使用任何.NET語言(例如VB.NET或C#)編寫。ASP.NET code-behind類負責處理相關聯的頁面事件。這個處理一個組件事件的Java類不必是頁面的支援JavaBean。過一會我們再討論它。值得注意的是,在ASP.NET中分離的code-behind檔案可能是不必要的-有可能在頁面本身的代碼中實現事件處理。然而,一般來說,這被認為是一種不好的編碼實踐,因為這樣以來將會導致混淆HTML和代碼問題。
下面是我們的樣本ASP.NET和JSF應用程式的兩幅快照。由於兩種組件的內在特性以及我也沒有對之應用匹配的視覺式樣,所以它們看起來略微有些不同。在兩個頁面中,顯示一個會議房間表格,還有進一步瞭解相應房間的View按鈕和預訂該房間的Reserve按鈕。
ASP.NET程式快照
JSF程式快照
上面這些組件都是通過拖動添加的,然後通過修改一個屬性面板定製它們的外觀和行為。當然,我還可以通過編輯它們在HTML源碼中的標籤來定製這些組件。在此不贅述。下面,我們重點分析一下在這些web頁面背後的代碼檔案,從而進一步分析事件代碼。
我認為,以表格形式顯示資料是不錯的開始,因為這種情況在應用程式是常見的。為此,ASP.NET和JSF也都沒有忽略這種實現,兩類被顯示的組件都提供了內建功能來實現諸如排序和分頁顯示等效果。在ASP.NET 2.0之前,ASP.NET就已經提供了許多資料顯示組件,其中DataGrid組件是使用最廣泛的。另外,ASP.NET 2.0發行中引入了一個新的GridView組件。在本例中,我使用了這種組件,因為它添加了一些新的有用的特性。ASP.NET組件利用ADO.NET技術,這也是整個.NET架構的資料存取技術-ADO.NET提供了一種健壯的物件模型來操作各種類型的資料來源。例如,Dataset對象允許你以一種斷開的方式來使用資料。這意味著,為了使用資料,你的應用程式並不需要連續地串連到資料庫上。一個Dataset還允許你隱蔽在它的介面後面的資料庫細節,從而使你以對應用程式的其它部分極小的影響來切換資料庫。我在本例中所使用的JSF組件是一個與Java Studio Creator一同發行的表格組件。它使用一個DataProvider對象-它允許你利用JDBC Rowset技術。JDBC Rowset還能使你在斷開的情況下以一種便於使用的方式使用資料庫。
每個這些啟動頁面都各自包含一個單一的標籤組件實現頁面頭部。在一個頁面的事件處理器中可以存取和修改該頁面組件。通過在可視化編輯器中簡單地雙擊該組件,你可以把大多數的事件掛接到一個組件上。而且,這將會把你導向代碼檔案中,在此你可以添加代碼。如我們前面已經注意到的,與web頁面相關聯的ASP.NET頁面類正是你的事件代碼將駐留的地方。在JSF中,並不象這樣簡單。JSF事件遵循Observer設計模式。需要被通知某些事件的對象都要把它們自己註冊為該事件相應的聽者(listener)。在JSF中共有兩種類型的事件:Value Changed事件和Action事件。典型地,Value Changed事件發生在例如列表框選擇這樣的行為中,而Action事件將產生例如按鈕點擊這樣的使用者行為。任何Java類都能夠響應於一個web表單的事件。然而,JSF頁面的支援bean是實現事件方法的很方便的位置,例如Sun Java Studio Creator就假定你把相應的實現放置於此。
當啟動一個ASP.NET應用程式時,位於一個檔案web.config中的配置資訊被分析和應用。每個ASP.NET應用程式都有一個web.config。我通過把串連串儲存到Mysql資料庫中來利用這個檔案。web.config經常用來儲存資料庫連接串以達到在代碼外保持這個串連。下面展示了web.config檔案的一部分代碼片斷:
<connectionStrings>
<add name="MyConnectionString"
connectionString="Driver={MySQL ODBC 3.51 Driver};server=localhost;database=test;uid=testuser;pwd=testpassword"
providerName="System.Data.Odbc"/>
</connectionStrings>
JSF應用程式依賴於典型的基於Servlet的Java應用程式架構。一個'WEB-INF'檔案夾下帶有一些子檔案夾和一個包含應用程式設定(很類於ASP.NET的web.config)資訊的檔案web.xml。注意,這裡的Mysql串連串並不儲存在這個JSF應用程式的web.xml檔案中。而是,Studio Creator自動地把該串連添加到伺服器的設定檔中,而我們的應用程式通過JNDI(Java命名和目錄介面)來存取它-這是一種搜尋J2EE服務的常用方式。每一個JSF應用程式的web.xml檔案都指定一個FacesServlet類型的Java Servlet。這個FacesServlet負責配置應用程式的相應於它的JSF使用的設定。在一個JSF應用程式中,所有的請求都要"流經"FacesServlet。這個控制項Servlet控制基於組件事件結果的應用程式頁面之間的流程。這被稱作"Front Controller"設計模式。在ASP.NET中沒有使用這種層級的間接方式,則是由頁面本身控制頁面流,稱作"Page Controller"設計模式。通過參考儲存在一個設定檔faces-config.xml中的頁面到頁面映射,這個FacesServlet知道如何路由請求。這個設定檔也包含一些我們將要強調的JSF配置資訊。
另外,ASP.NET和JSF頁面擁有非常相似的生命週期。在前面,我們已經討論了組件是如何基於使用者行為建置事件的,而事件僅是這種生命週期的一部分。從一種進階視圖角度來觀察一個頁面的生命週期,其大致流程為:使用者發出一個請求並初始化頁面,然後,頁面的整個組件樹被讀入並且組件的狀態被恢複,根據使用者行為處理事件並更新群組件值,最後結果頁面被產生到使用者端。當然,實際情形可能比這更為複雜些,而且還有一些重要階段(例如資料校正)摻雜在這些步驟中。在JSF和ASP.NET之間的一個關鍵區別在於,組件如何被產生到使用者。ASP.NET組件在頁面上產生自己;而在JSF中,組件能夠自我產生,但是更為經常的是,它們把組建代理程式到特定的Renderer對象(可用於一個RenderKit中)。針對每一種不同類型的描述媒體提供一種不同的RenderKit。每一個JSF實現都會提供一個預設的HTML RenderKit。這意味著,同一個JSF組件可能被以不同形式產生到一種web瀏覽器或無線裝置上-通過簡單地加入一個不同的Render對象,這是一種相當強的產生能力。
一個很方便的添加代碼而又不依賴於任何特定類型的使用者請求的時機是當頁面對象被初始化時。ASP.NET提供了一個Page_Load事件-它當使用者作任何類型的頁面請求時被調用。在JSF頁面中,你可以使用類構造器方法來實現同樣的頁面初始化邏輯。下面是兩種應用程式的相應於使用者在資料顯示組件上所做動作的事件處理代碼。
//ConferenceRooms.Java事件代碼:
public String btnViewReservations_action() {
//把選定行的相應的房間的id和name儲存起來...
Object id = getValue("#{currentRow.value['conference_rooms.room_id']}");
Object name = getValue("#{currentRow.value['conference_rooms.room_name']}");
ReservationsSessionBean resBean =(ReservationsSessionBean)this.getBean("ReservationsSessionBean");
resBean.setRoomId(id.toString());
resBean.setRoomName(name.toString());
return "view";
}
public String btnMakeReservation_action() {
Object id = getValue("#{currentRow.value['conference_rooms.room_id']}");
ReservationsSessionBean resBean = (ReservationsSessionBean)this.getBean("ReservationsSessionBean");
resBean.setRoomId(id.toString());
return "reserve";
}
//ConferenceRooms.cs事件代碼
protected void GrdVwRooms_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName.Equals("Sort")) {
SortDirection sd;
if (((SortDirection)Session["sortRooms"]).Equals(SortDirection.Ascending)) {
sd = SortDirection.Descending;
}
else {
sd = SortDirection.Ascending;
}
Session.Add("sortRooms", sd);
this.GrdVwRooms.Sort(e.CommandArgument.ToString(), sd);
}
else
{
DataKey data = GrdVwRooms.DataKeys[Convert.ToInt32(e.CommandArgument)];
Session.Add("roomId", data.Values["room_id"].ToString());
Session.Add("roomName", data.Values["room_name"].ToString());
if (e.CommandName.Equals("Reserve")) {
Server.Transfer("Reserve.aspx");
}
else
{
if (e.CommandName.Equals("View")) {
Server.Transfer("RoomReservations.aspx");
}
}
}
}
這裡的代碼有一點不同,Java類被分成了兩個方法。這兩個Java事件都是Action事件。我把這個ASP.NET應用程式中的所有的事件邏輯都納入到一個單一的RowCommand事件中並且查問該事件的參數來決定哪一個使用者事件發生。在這個特殊的事件處理器中,我實現了view和reserve按鈕點擊相應事件,還有針對於ASP.NET組件情況的排序請求。兩種應用程式都儲存roomId和roomName屬性以便於應用程式的後面使用。具體請參考本文相應源碼中的ConferenceRooms.java和ConferenceRooms.cs檔案。記住,這些類檔案中的大多數代碼都是工具產生的,你可以使用編輯器提供的代碼重疊功能來使代碼更具可讀性。在這些事件方法中,你可以立即存取這個頁面組件的值以實現讀取和修改。ASP.NET和JSF儲存檢視狀態-通過把它寫到隱藏的HTML域中並把它從一個請求傳遞到另一個請求。當然,使用相同的方法來儲存狀態的傳統型ASP和JSP應用程式仍然是可用的。例如,在ASP.NET和JSF頁面中,房間id和name都被放置到使用者的Session中,這樣它就可以在後面被檢索。
注意,在上面的事件代碼中,ASP.NET檔案調用一個方法"Server.Transfer"實現把控制傳遞給另一個頁面。然而,在JSF事件代碼中,你不會找到這樣的到另一個頁面的直接的參考,而僅有返回串"view"和"reserve"。這是因為,JSF中經由我們更早討論的faces-config設定檔處理頁面流問題,任何使用過開源架構Struts的Java開發人員應該對此很熟悉。在ConferenceRooms.jsp檔案的faces-config.xml檔案中共有兩個入口-字串"view"和"reserve"。在運行時刻,應用程式使用這些字串查詢頁面地址。下面是在ConferenceRooms.jsp頁面的faces-config檔案中發現的XML形式的導航規則。我是使用Java Studio Creator中的一個視覺化設計工具建立的這些映射。
<navigation-rule>
<from-view-id>/ConferenceRooms.jsp</from-view-id>
<navigation-case>
<from-outcome>view</from-outcome>
<to-view-id>/RoomReservations.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>reserve</from-outcome>
<to-view-id>/ReserveRoom.jsp</to-view-id>
</navigation-case>
</navigation-rule>
視覺化設計工具展示了在Java Studio Creator中的導航規則
這個faces-config檔案也是你列舉與你的JSP分頁檔相關聯的所有Java Bean的地方。事實上,你可以通過這個檔案配置任何你的應用程式需要參考的Java對象-這些Java對象被參考為'managed beans'。所有的JSF支援bean都將在這個設定檔中被列舉為託管bean,但是任何你需要的其它對象也都可以在這裡找到。一個託管bean包含完整類名、該bean(是把它儲存在應用程式的Request,Session還是應用程式級)的使用範圍以及當參考這個bean時要使用的名字。在下面的XML中,列舉出了初啟web頁面的支援bean,還有一個名為ReservationsSessionBean(我們使用它來共用應用程式中的資料)的EJB的入口:
<managed-bean>
<managed-bean-name>ReservationsSessionBean</managed-bean-name>
<managed-bean-class>webreservations.ReservationsSessionBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>ConferenceRooms</managed-bean-name>
<managed-bean-class>webreservations.ConferenceRooms</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
讓我們再回到前面的應用程式中來,我們的查看預訂資訊的請求把我們引向ASP.NET版本的RoomReservations.cs.aspx(相應於JSF版本的RoomReservations.jsp)。在相關的代碼檔案(RoomReservations.cs和RoomReservations.java)中,該事件代碼檢索儲存於前一個頁面中的房間id並且使用它來過濾第二個表格(它列舉有關該房間的預訂情況)。在資料表格上面的標籤被修改以引用相應房間的名字。
ASP.NET
>
JSF
一個delete按鈕相應於刪除每一行資料,這兩個組件都允許你輕鬆地修改表中的資料。最後,每一個表單都包含一個按鈕-它把你導向另一個表單以添加對該房間的新的預訂。這些表單顯示於下面圖中。注意,在圖中由Java Studio Creator和Visual Studio實現的行事曆群組件在顯示上有所不同,但是實現相同的目的。
ASP.NET
JSF