在Jsp Model 2模型中, 用戶的所有請(qǐng)求提交給Controller Servlet,
由Controller進(jìn)行統(tǒng)一分配, 并且采用推的方式將不同的UI顯示給用戶。
這種推方式在很多人看來是一種優(yōu)點(diǎn),因?yàn)樵赟truts等MVC實(shí)現(xiàn)中具體推送的UI可以在配置文件中配置,配置完成后還可以通過一些可視化分析工具得到
整個(gè)站點(diǎn)地圖。在Model2模式中基本的訪問格式為:
action.do?其他參數(shù)
我
本人從未應(yīng)用過Model2模式,但與我們的jsplet框架對(duì)比,我認(rèn)為這種推送方式在大多數(shù)情況下并不是什么優(yōu)點(diǎn)。如果將一次web訪問看作是一次函
數(shù)調(diào)用,則按照Model2模式,這個(gè)函數(shù)的返回情況是不確定的,需要由一個(gè)額外的配置文件來確定。而我們知道,一個(gè)返回情況不確定的函數(shù)一般不是什么良
好的設(shè)計(jì)。在我們的框架設(shè)計(jì)中,一個(gè)基本的觀點(diǎn)是盡量將自由度暴露給實(shí)際控制它的人。實(shí)際上,在大多數(shù)情況下,頁面編制人員知道應(yīng)該使用哪個(gè)頁面來顯示數(shù)
據(jù),他們并不需要一個(gè)額外的配置文件。Jsplet使用如下的url格式:
視圖jsp?objectName=模型對(duì)象名&objectEvent=響應(yīng)事件名&其他參數(shù)
舉一個(gè)具體的例子:
http://my.com/demo_view.jsp?objectName=/@Demo&objectEvent=test
demo_view.jsp是指定的顯示頁面, 其代碼如下:
[code]
<%@ include file = "/engine.jsp" %>
<!-- 相當(dāng)于在jsp模型中增加了一個(gè)新的變量thisObj,從而實(shí)現(xiàn)jsp頁面的對(duì)象化 -->
<c:out>${thisObj.testVar}</c:out>
[/code]
objectName被WebEngine映射到session中的一個(gè)對(duì)象,在demo_view.jsp中成為thisObj這個(gè)變量,這就相當(dāng)于java語言中的this指針,從而實(shí)現(xiàn)了jsp頁面的對(duì)象化。
WebEngine還將objectEvent映射到一個(gè)Action響應(yīng)函數(shù)并自動(dòng)調(diào)用它,具體的Action代碼寫在一個(gè)獨(dú)立的java文件或者jsp文件中。
DemoAction.jsp
[code]
<%@ include file = "/jsp_action_begin.jsp" %>
<%!
//
// objectName映射為thisObj, objectEvent=test映射對(duì)actTest的調(diào)用
// 在這里增加一個(gè)actXXX函數(shù)之后,即可通過objectEvent=XXX來訪問,不需要任何配置
public Object actTest(){
// thisObj中的變量可以在視圖中使用
thisObj.set("testVar","hello");
return success();
}
// 如果存在actBeforeAction函數(shù),則該函數(shù)在所有action函數(shù)之前調(diào)用
public Object actBeforeAction(){
return success();
}
// 如果存在actAfterAction函數(shù),則該函數(shù)在所有action函數(shù)之后調(diào)用
public Object actAfterAction(){
return success();
}
%>
<%@ include file="/jsp_action_end.jsp" %>
[/code]
在Jsplet框架中只需要注冊(cè)對(duì)象,而不需要單獨(dú)注冊(cè)每個(gè)action。
register.jsp
[code]
<%
WebEngine.registerType("Demo", new WebActionType("/demo/action/DemoAction.jsp"),pageContext);
%>
[/code]
與Jsplet
框架對(duì)比,Model2是對(duì)action的建模而不是對(duì)object的建模,即它相當(dāng)于將objectName,objectEvent和
view.jsp綁定在一起定義為一個(gè)訪問點(diǎn)action.do,綁定過程中需要一個(gè)配置文件來固化view.jsp和action之間的聯(lián)系。因此,
Model2并沒有完全分離view和model,它隱含假定著objectName只具有一個(gè)objectEvent,
并且綁定了一個(gè)具體的view(出錯(cuò)頁面除外)。
例如, 我們需要兩個(gè)不同的view來顯示同一個(gè)數(shù)據(jù),則在Model2程序中可能需要配置兩個(gè)獨(dú)立的訪問點(diǎn),而在我們的框架中只需要使用兩個(gè)不同的url:
a_view.jsp?objectName=/@Demo&objectEvent=test
b_view.jsp?objectName=/@Demo&objectEvent=test
同樣的web程序甚至可以在前臺(tái)通過XMLHTTP方式來調(diào)用而不需要額外配置!
在Jsplet框架中采用的是對(duì)象化的方式而不是Action化的方式,因此存在著多種面向?qū)ο蟮臄U(kuò)展,而所有的擴(kuò)展都直接體現(xiàn)在url格式的細(xì)化上,一切都在陽光下。
在Jsplet中objectName是WebObject的名稱,在全系統(tǒng)內(nèi)唯一,其格式定義為: objectScope@objectType$objectInstanceId
1. 對(duì)象類型objectType
我們需要注冊(cè)的是對(duì)象類型而不是完整的對(duì)象名,一個(gè)對(duì)象類型可以對(duì)應(yīng)于無數(shù)個(gè)完整的對(duì)象名,例如我們注冊(cè)了Demo類型的WebObject, 則objectName=/@Demo和objectName=/left/@Demo對(duì)應(yīng)的處理文件都是DemoAction.jsp。
2. 對(duì)象生命周期控制objectScope
objectScope為WebObject所在的域,其格式符合Unix路徑命名規(guī)范。JSP模型本身支持一些預(yù)定義的對(duì)象域,包括page,
request, session,
application等。但為了能夠反映現(xiàn)實(shí)世界中的對(duì)象組織結(jié)構(gòu),對(duì)象域必須是允許自定義的。objectScope被組織成一個(gè)樹形結(jié)構(gòu),這是一個(gè)
基本的控制結(jié)構(gòu),其控制策略為
同時(shí)存在的對(duì)象域之間必須存在線性序關(guān)系(order)
當(dāng)系統(tǒng)訪問某一對(duì)象時(shí),如果該對(duì)象所在的對(duì)象域不能和現(xiàn)有對(duì)象的域處在同一"路徑"下(即當(dāng)對(duì)象域之間不能建立父子關(guān)系時(shí)),系統(tǒng)就會(huì)自動(dòng)銷毀不兼容路徑
分支下的所有對(duì)象。 這種精細(xì)的控制策略保證了系統(tǒng)的可擴(kuò)展性,因?yàn)槟P蜕峡梢员WC始終只有一部分對(duì)象被創(chuàng)建。
對(duì)象轉(zhuǎn)移
系統(tǒng)動(dòng)作
/main/@MyObject ==> /main/left/@OtherObject
無
/main/left/@OtherObject ==> /main/@MyObject
無
/main/left/@OtherObject ==> /main/left/@MyObject 無
/main/left/@OtherObject ==> /main/right/@MyObject 自動(dòng)銷毀/main/left子域下的對(duì)象,如/main/left/@OtherObject
3. 對(duì)象實(shí)例標(biāo)識(shí) objectInstanceId
如果在某一對(duì)象域中需要包含多個(gè)同一類型的對(duì)象,可以通過objectInstanceId來加以區(qū)分,這樣在同一個(gè)頁面上我們可以使用多個(gè)同樣類型的對(duì)象。
Jsplet中另外一個(gè)擴(kuò)展是通過事件路由來支持jsp子頁面的對(duì)象化。例如
http://my.com/demo_main.jsp?objectName=/@Main&eventTarget=/@Sub&objectEvent=test
如果指定了eventTarget參數(shù),則objectEvent由eventTarget對(duì)應(yīng)的對(duì)象來響應(yīng)。
在jsp文件內(nèi)部我們可以通過include語法來引入子對(duì)象,例如
<jsp:include page="sub_view.jsp?objectName=/@Sub" />
(注:我不是非常清楚Tapestry具體是如何實(shí)現(xiàn)對(duì)象化的,熟悉Tapestry的朋友可以介紹一下)
在Jsplet中可以通過配置文件來支持對(duì)Action的interception, 例如
[code]
<factory>
<listener-filter class="global.LogFilter" />
<post-listener class="global.CommonActions"/>
<type name="Demo">
<!-- 如果未指定object, 則缺省為WebObject類型 -->
<object class="demo.MyWebObject" />
<listener>
<filter event="query*|select*" class="demo.LogFilter" />
<url-listener url="/demo/DemoAction.jsp" />
<url-listener url="/demo/DemoAction2.jsp" />
</listener>
</type>
</factory>
[/code]
在上面這個(gè)配置文件中,DemoAction.jsp和DemoAction2.jsp是chain關(guān)系,即事件響應(yīng)的傳播模型中,如果event沒有被標(biāo)記為stopPropagation,就會(huì)傳遞到下一個(gè)listener。
綜上所述,可以看到在目前多變的需求環(huán)境下,Model 2已不是一種非常完善的Web程序模式,一些重要的設(shè)計(jì)需求在Model 2模式的推方式中很難得到合適的表達(dá)。