starting struts2 翻譯 第4章 架構的目標
對基于特定代碼的應用來說,其架構目標很難確定下來。在開始開發之前制定文檔化的目標,這種情況只是空想,因為一旦開始開發,代碼通常就會朝一個與架構目標不同的方向前進,這樣實際上最后的應用特征是基于代碼而不是架構。這種情況很難發現并且是開發過程中逐步形成的產物而不是計劃的產物,其結果就會造成不同的程序包及特性之間的不一致。
本章我們將會討論5種基于Struts2代碼的特征。從2002年以來,這些架構元素隨著代碼的發展仍然存在--從開始的WebWork,經過WebWork分解為WebWork2和XWork,一直到最后轉變為Struts2.
4、1 概念的分解
作為一個web應用開發人員,有多種級別的功能需要進行闡述:
(1)在request/response周期期間,需要對作為其核心目標的每個action的邏輯進行說明;
(2)訪問或擁有可以用來執行action邏輯或訪問資源的業務對象;
(3)編譯、映射和轉換,以便把HTML中基于字符的值轉化為原始類型,以及把視圖對象轉換為業務對象或數據表來進行表示;
(4)為應用中分組的action或所有的action提供功能的橫切關注點。
在Struts2架構中,每一個概念都是獨立的。功能和邏輯不需要放置在專門的action里。我們來看看上面所提到的概念以及它們是如何處理的:
(1)每個Action邏輯--這是最簡單的概念。每個action負責它需要提供的邏輯或功能;
(2)編譯、映射和轉換--3者是各自有所不同的概念,但是有一個相同點就是它們都是作為核心action邏輯的補充。編譯和類型轉換由框架本身進行處理。來自于HTML的字符值在action開始處理之前被轉換為基本類型或被注入到action中--所有需要的都在此處。映射由一個特定的攔截器進行處理。通過對一個action進行配置,說明其所擁有的域模型,并準確指明其在HTML中所對應的域,框架將會把UI映射到該域模型上。它甚至會生成對象圖。
(3)橫切關注點--攔截器是提供橫切功能的主要角色。開發者可以實現攔截器,然后把它們應用到所有action、某個特定包中的所有action或者選擇某些指定的action中去。另一個橫切關注點是用戶接口層。Struts2可以使用其所提供的稱為“主題(themes)”的標簽來為此提供幫助。不同的主題可以被開發來提供不同的層選項,并被應用到單個標簽或整個應用(通過把它設為默認值)。
4、2 松耦合
WebWork的早期目標之一就是提供一個松耦合的框架。其2.0版更加強調了這點:把代碼分成了兩個項目,一個是XWork--提供普通的命令模式(generic command-pattern)框架,另一個是WebWork--為XWork提供明確的web接口。這個WebWork架構基礎上的變化創建了二者的共生關系。原來賴以成名的“WebWork”現在實際上是WebWork和XWork的結合體。
作為一個獨立的項目,XWork現在可以被用于其它項目的一部分了--實際上也確實是這樣。Swingwork就是一個這樣的項目--它是一個基于Swing的、底層使用XWork的MVC框架。另外一個例子就是JMS前端--執行或共享與web UI相關的XWork action。這些例子提供了高水平松耦合的典型案例。Struts2是又一個采用XWork的項目。
松耦合思想已經更近一步,集成到整個框架--從處理action的第一步直到最后一步。實際上,在Struts2里幾乎沒有什么不可配置的--個人認為這個特點既是Struts2發展的極大原動力之一,但同時也是其重大缺陷之一。
松耦合配置的一般案例包括:
(1)把URL映射到action;
(2)把一個action的不同結果映射到提交的頁面;
(3)把處理期間產生的異常映射到提交的異常頁面;
不常使用及Struts2特定的案例包括:
(1)如果你不想使用Sping框架,就得配置業務對象工廠;
(2)改變URL映射到一個action類的方式;
(3)為action結果增加新的結果類型;
(4)為新的框架功能增加插件;
(5)通過攔截器配置框架級功能;
松耦合系統的好處是眾所周知的--增強了可測試性、易于擴展框架特征等。但有一個缺陷,由于可置配的靈活性(尤其是與攔截器相關的),導致一個特定action的處理路徑不易被開發者所理解。這一點在進行調試的時候比較明顯。由于不理解發生了什么事,一個不了解系統的開發人員將不能快速高效的進行調試。問題本身可能比較簡單,比如由一個沒有進行正確配置的攔截器引起的,或者甚至只是因為攔截器的順序不對引起的。只要理解處理路徑上的每一個細節,很快就會找到問題的解決辦法。
4、3 易測試性(Testability)
在過去幾年中,單元測試已經成為軟件開發過程中事實上的標準。它不僅保證了類邏輯的一致性,而且通過在類開發過程中甚至開發之前實現單元測試,將會減少設計的復雜性,并且會使設計更為健壯。
Struts2的前身,WebWork就是建立在這種環境之上的。由于框架元素的松散耦合,測試很容易進行。在web應用開發過程中,action、攔截器、結果、對象工廠及其它被開發的組件能夠獨立地進行測試。由于action和攔截器最為常用,我們就對這兩種組件進行詳細的探討。
Actions
通常在框架中是通過執行“execute()”方法來調用action的,或者在配置時調用任何一個返回值為字符值(String)的方法來進行調用的。從易測性的角度來看,這再簡單不過了。
我們來看一個例子。下面是一個數字累加的action類:
public class MyAction {
private int number;
public int getnumber() { return number; }
public void setNumber( int n ) { number = n; }
public String execute() {
number += 10;
return “success”;
}
}
由于該action是POJO,單元測試只需把action實例化、調用方法,并在斷點出得到預期的結果就可以了。所有搜數據和資源都通過setter方法提供給action。因此action需要的任何數據都能夠直接在action里進行設置。
在本例中我們需要兩個斷點--一個為“execute”方法的輸出進行設置,另一個則是為驗證action的狀態是否是我們預期的。單元測試如下所示:
public class myActionTest extends TestCase {
…
public void testExecute() {
MyAction action = new MyAction();
Action.setNumber(5);
assertEquals("success", action.execute());
assertEquals(15,action.getNumber());
}
}
對于資源稍微有點麻煩,類似jMock這樣的庫可以被用來提供資源的模擬實現,測試在action和資源之間的交互是否正確。
盡管本例是用JUnit寫成的,但是TestNG或其它任何框架都可以使用。
攔截器(Interceptors)
當你編譯攔截器的時候,測試會稍微有點復雜。不過也有一些額外的幫助可以利用。使用攔截器進行工作有兩種情景。
第一種情景是使用在調用時與ActionInvocation對象進行交互的攔截器。在執行后,你能夠通過在斷點出得到攔截器本身的狀態來驗證邏輯的正確性。在這種情景下,你能夠利用與測試action同樣的方法來測試攔截器。實例化攔截器、創建一個ActionInvocation對象的模擬實現(該對象帶有在測試攔截器時將會用到的值)、調用intercept方法、在斷點處得到預期的結果。這些可能發生在攔截器本身、被調用方法的返回結果或是由系統拋出的異常。
第二種情景是使用與其環境或攔截器堆棧中的其它攔截器發生交互的攔截器。在這種情況下,測試需要通過ActionProxy類與action進行交互,還將會需要訪問攔截器本身不能訪問的其它環境對象。
XWork庫通過提供XWorkTestCase類來為JUnit測試提供幫助、通過提供TestNGStrutsTestCase 和TestNGXWorkTestCase類來為TestNG測試提供幫助。這些為ConfigurationManager,Configuration, Container 和 ActionProxyFactory類的實例提供了測試實現。涉及到的類還有XWorkTestCaseHelper 和 MockConfiguration.等。
現在我們已經具備了安裝環境的基礎,測試本身變得容易了--遵循在第一種情景里所描述的步驟即可。唯一的不同點在于,不是調用攔截器的intercept()方法,而是需要調用ActionProxy類的execute()方法。如下述代碼所示:
ActionProxy proxy =
actionProxyFactory.createActionProxy(NAMESPACE,NAME,null);
assertEquals("success", proxy.execute());
在這種情況下,測試將會設置斷點來獲取預期的action結果值、action值或值堆棧的值。被執行的action能夠在執行前或執行后通過下面的調用進行訪問:
MyAction action=(MyAction)proxy.getInvocation().getAction();
而值堆棧則可以通過下面的調用進行訪問:
proxy.getInvocation().getStack();
4、4 模塊化(Modularization)
隨著應用系統越來越龐大,把web應用分離為許多模塊就變得越來越重要。模塊化允許在一個項目中開發的功能或新的框架特征能夠獨立打包,并可以在其它項目中進行重用。Struts2已經把模塊化作為其架構的基礎部分,允許開發者獨立工作和編譯彼此的項目。
下面是一些對應用進行模塊化的方法:
(1)配置信息能夠被分為多個文件--這并不影響應用的打包,由于配置信息根據功能界限進行邏輯分離而易于查找,從而簡化的開發人員的工作。
(2)獨立的應用模塊可以以插件的形式進行創建--為了提供摸個特定特征所需的一切都可以打包在一起并作為插件獨立發布。這包括action、攔截器、攔截器堆棧、視圖模板(JSP除外)等。瀏覽器插件的配置就是一個例子,該插件提供了一個完整的模塊,當你把該模塊添加到你的應用中時,其提供了一個web接口來查看配置信息。
(3)可以創建新的框架功能插件-非特定應用的新功能可以以插件形式捆綁并在不同的應用中使用。
從技術角度來說,所有這些對應用進行模塊化的方式都是一樣的--都具有同樣的配置元素(除了名稱不同之外,“struts-plugin.xml”是系統自動加載插件時的配置文件)、具有相同的目錄結構,并且它們也包含同樣的框架和應用元素。
插件的兩種類型之間的唯一不同就是你在概念上如何看待它們,以及哪些元素和配置被放入分發包中。
由于插件為核心框架功能提供了可選實現,所以有一些額外的配置元素。盡管在“struts.xml”和“struts-default.xml”配置文件中可以使用這些元素,但是通常情況下它們還是在插件配置文件中進行配置。
對于插件來說,可選實現的配置分為兩步:
1、使用<bean .../>標簽中來提供可選接口的實現,使用一個唯一的鍵來識別它。
2、使用<constant … />標簽在已配置的接口實現中選擇一個。
我們來更詳細地看一下這兩個步驟。
<bean .../>標簽允許插件提供擴展點的實現信息。下面的例子就是一個展示在"struts-default.xml"文件中為一個對象工廠進行配置的情況:
<bean name="struts"
type="com.opensymphony.xwork2.ObjectFactory"
class="org.apache.struts2.impl.StrutsObjectFactory" />
屬性提供了在Struts2中創建和使用一個可選對象實現的所有信息。可以使用的屬性如下所示:
class--提供類的全名;
type--指明類需要實現的接口;
name--對每個類進行唯一識別的簡稱;
static--指明是否把靜態類方法注入到類的實例;
scope--指明所用實例的范圍,其值可能為:"default"、"request"、"session"、 "singleton"或"thread"。
optional--如果值為“true”,既是在創建類實例時出現錯誤,將仍然繼續加載。
接下來,<constant … />標簽允許開發人員選擇使用哪個配置。只有兩個屬性--一個屬性“name”提供了你的新實現所改變的擴展點的名稱,另一個屬性“value”就是在<bean … />標簽中配置的“name”名稱。
<constant name="struts.objectFactory" value="plexus" />
<constant … />標簽是把一個新值賦給一個已知屬性的一種方式。該值也可以在"web.xml"配置文件中使用“init-param”進行修改,或者在““struts.properties”配置文件中作為一個名值對(name-value pair)進行修改。
如果你沒在開發一個插件,而只是使用常規"struts-xml"配置文件中的這些技巧,這里還有一個捷徑。在<constant ... />標簽里,使用你通常放在<bean ... />標簽里的類值---這樣做可以避免對<bean ... />標簽的需求。
下面這張表列出了可配置擴展點的接口和屬性名稱:(表省略)
慣例優先配置的原則是Rails帶給主流應用開發的一個概念。不是提供在很多應用中都很類似的配置文件,而是假定在多數情況下開發人員將會遵從一個特定的模式。這個模式非常普遍以至于可以被認為是一個慣例,這樣就可以在框架中提供一個默認的配置而不是為每個新的應用提供一個配置。默認情況下,開發人員不需要提供配置信息。但是如果有與慣例配置信息不同的需要,就得提供相應的配置信息來代替默認配置信息。
Struts2已經采用了這種理念。松耦合給Struts2提供大的彈性,但是同時這也意味著該框架很難進行配置。而慣例優先配置原則則為這兩種對立的力量提供了平衡,使開發人員的開發可以更加簡單和高效。
在Struts2中,慣例優先配置的例子包括:
(1)
隱式配置文件加載—配置文件“Struts-default.xml”和“struts-plugin.xml”是自動加載而不是顯式加載的。
(2)
插件中的代碼—當利用插件中的代碼時,使用action名稱和結果字符二者的結合來自動搜索結果模板,所以對于“/user/add.action”來說,將會為一個“success”的結果返回“/user/add-success.jsp”模板,而為一個“error”的結果返回“/user/add-error.jsp”模板。
(3)
默認結果及結果類型—當對action進行配置時,當使用默認的“success”和JSP時,不必指明結果和類型。
(4)
綁定Spring業務服務—當安裝了Spring框架插件后,不必為每個action配置其所需的基于Spring提供的業務,因為這些業務會自動與action進行綁定。
在前面的章節中,我們已經看到了幾個默認配置,以及如何通過配置使用新值來代替默認值。更多的配置選項,以及更多的慣例將會在接下來的章節中進行探討。
posted on 2007-07-25 17:23 leefoo 閱讀(721) 評論(0) 編輯 收藏 所屬分類: struts2相關翻譯