詳談 Struts2 的核心概念
本文將深入探討Struts2 的核心概念,首先介紹的是Struts2 的體系結構和幾個重要的配置文件,并會舉例說明Struts2 的核心對象如何配置。然后介紹Struts2 最重要的3 個組成部分Action 、Result 、Interceptor (攔截器)的原理和使用方法。
1? 術語概述
?????????? Action 在Struts2 中是負責Web 應用程序中具體邏輯實現的。Action 是一個Java類,一般的繼承于com.opensymphony.xwork.ActionSupport 類,這個類在Struts2 的Dispatcher 接受到HTTP 請求的時候被調用。
?????????? 當一個action 執行完畢之后,它將返回一個返回碼,譬如“SUCCESS ”“INPUT ”或者其他“返回代碼”。這些“返回代碼”通過查找struts.xml 中的定義告訴Struts2 下一步該做什么而這個下一步就稱為result 。Struts2 支持許多種不同的result 類型,比如返回結果頁面給用戶。可選擇的顯示模板技術有JSP 、Velocity 或者是FreeMarker 。
2? Struts2 的體系結構
?????????? Struts2 的核心體系結構如圖5.1 所示。用戶在Struts2 框架下只需編寫自己的Action 類來處理邏輯、編寫JSP 頁面(或者其他方式)來展示用戶界面和在struts.xml 配置映射關系就可以完成基本的業務流程。
3? Struts2 的配置文件
?????????? Struts2 框架主要有兩個核心配置文件:struts.properties 和struts.xml 。struts.xml 與Struts1 版本中的struts-config.xml 非常類似,主要負責管理應用中的Action 映射,以及該Action 包含的Result 定義等,而struts.properties 文件則定義了Struts2 框架的全局屬性。所有的配置文件說明見表所示,圖展示了幾個配置文件所在位置和相互關系。
3.1? 全局配置文件--struts.properties
?????????? struts.properties 文件是一個標準的Properties 文件,該文件包含了系列的key-value 對象,每個key 就是一個Struts2 屬性,該key 對應的value 就是一個Struts2 屬性值。struts.properties 文件通常放在Web 應用的WEB-INF/classes 路徑下,實際上,只要將該文件放在Web 應用的CLASSPATH 路徑下,Struts2 框架就可以加載該文件。以下是一部分配置片段。
?????????? struts.enable.DynamicMethodInvocation=false"
?????????? struts.devMode=false
3.2? 核心配置文件--struts.xml
?????????? struts.xml 文件主要負責管理應用中的action 映射,以及該action 包含的result 定義等。在struts.properties 配置中的有一項struts.configuration.files ,這里可以看出struts.xml 這個文件名不一定是固定的,可以配置為其他文件名的。struts.xml 內容主要包括:Action 、Interceptor 、Packages 、Namespace 等。后面的章節將詳細介紹如何配置這些元素。
?????????? 在struts.xml 中可以使用 標簽把內容分到幾個文件中去。這里非常像JSP 中的 動作標簽,可以把其他文件的內容導入進來,被導入的每個配置文件必須和struts.xml 文件有一樣的格式。 標簽的格式如下所示。
3.3? struts.xml 的缺省實現
?????????? struts-default.xml 這個文件被包含在struts2-core.jar 中,文件名已經可以看出這個文件的作用是struts.xml 的缺省配置,它將自動被加載然后導入到struts.xml 中去。代碼5-1 是struts-default.xml 的部分片段。
3.4? Velocity 模板規則文件
?????????? 如果在程序中使用了Velocity (一個基于java 的模板引擎,可以替代JSP 作為顯示頁面)可以把文件velocity.properties 放到classpath 中去,系統將自動加載。同時還要配置struts-default.vm 文件,
?????????? 代碼? velocity.properties
?????????? # Velocity 資源定義.
?????????? velocimacro.library = action-default.vm , tigris-macros.vm , myapp.vm
4? struts.xml 的配置
?????????? Struts2 絕大多數的配置都是在struts.xml 中完成的,學習struts.xml 文件是學習使用struts2 的基礎。本節將詳細講述如何在struts.xml 中定義和配置各種元素。
4.1? action 配置
?????????? action 是Struts2 的基礎“工作單元”。配置一個基本的action 只需要兩個信息:action 名字和對應的action 類,這兩部分就建立了一個最簡單的action 配置。屬性“method ”用來告訴Struts2 調用action 的那個方法。在action 處理之后一般的需要展示處理結果給用戶,所以還需要把action 和result 映射在一起。如代碼所示。
?????????? 代碼? Action 配置:struts.xml
? ? ? ? ? <! —Struts action 配置-->
?<action name="helloWorld" class="example.HelloWorld" method= ”doWork ”>
?<result name="failure" path="Error.jsp"/>
?<result name= ”ok ” path="HelloWorld.jsp"/>
?</action>
4.3? 攔截器(interceptor )配置
?????????? interceptor 是能在一個action 執行的前后執行的代碼。它是做Web 應用程序時很有用的工具。最常見的由Interceptor 實現的功能如:安全檢查( 確保訪問者是登陸用戶) 、跟蹤日志( 記錄每個action) 、效率瓶頸檢查( 記錄每個action 開始和結束的時間以檢查程序中的瓶頸) 。也可以把interceptor 連在一起組成interceptor 棧(interceptor-stack )。比如在action 執行前同時做登陸檢查,安全檢查和記錄日志,可以定義一個interceptor 的棧。interceptor 必須事先定義好,然后可以連在一起組成一個棧。如代碼5-7 所示,定義了一個interceptor 和一個interceptor 棧。
4.4? 包(package )配置
?????????? 所謂packages 就是把actions 、results 、results 、types 、interceptors 這些元素打包到一個邏輯單元中去,從概念上講,packages 就更像一個程序中的對象,可以被其他子包從寫,而且可以擁有自己獨立的部分。Name 屬性是packages 的必填元素,它作為一個關鍵字被后邊的包引用;extends 元素是可選的,它允許包擴展一個和多個前邊定義的包。Abstract 元素是可選的,如抽象類和抽象函數一樣它是必須被繼承的,可以申明一個不包含actions 的package 。
4.5? 命名空間(Namespace )配置
?????????? 命名空間屬性允許把action 配置分成不同的命名空間,這樣可以使功能不同action 中使用相同的名字。默認命名空間用“”( 空字符串) 表示。如果系統在指定的命名空間中沒有找到某個action ,就會到默認命名空間中查找。可以在所有用"extends" 擴展的命名空間外配置全局action 不指定命名空間。
?????????? Struts2 中有以“/ ”命名的根命名空間,它是請求直接來自應用程序根路徑的時候的命名空間。和其他命名空間一樣,如果在根命名空間中沒有所需的action 別名,系統會回到默認命名空間中查找。如代碼5-9 所示,這里使用了默認命名空間、“/ ”和聲明了的命名空間“barspace ”
4.6? 在struts.xml 中定義Bean
?????????? 在struts.xml 中還可以作JavaBean 的定義如下: s
<!-- 在struts.xml 中定義Bean --->
<struts>
<bean type="com.opensymphony.xwork2.ObjectFactory"
name="myfactory"
?class="com.company.myapp.MyObjectFactory" />
</struts>
4.7? 在struts.xml 中使用通配符
?????????? 當配置文件中action mapping 的數量很多的時候,使用通配符是一個很好的辦法,可以將一些相似的mapping 綁在一起,用一個比較通用的mapping 來表示。在路徑中用* 來代替變化的部分,而action 的處理類和JSP 中{1} 剛好是代替這個變量。
?? ? ? ? <!-- 在struts.xml 中使用通配符-->
<action name="/edit* ” class="example.Edit{1}Action">
<result name="failure" path="/mainMenu.jsp"/>
<result name= ”ok ” path="http://{1/}.jsp"/>
</action>
</result>
5? 實現Action
?????????? Action 是Struts2 編程的核心部分,反映了對Web 應用程序的功能需求。Action 在MVC 模式中擔任控制部分的角色,在Struts2 中也使用的最多。每個請求的動作都對應于一個相應的action ,action 還可以負責存儲數據/ 狀態(以getter 和setter 的方式)并且執行邏輯處理。
?????????? 在本章中將關注如何實現action ,以及action 如何提供Web 應用程序中所需的通用功能。除了Action 接口之外,Struts2 的action 也可以選擇實現其他可選擇的接口,從而使action 能夠提供諸如國際化、校驗、負責工作流和錯誤信息處理等功能。ActionSupport 基類實現了Action 接口并提供了大部分可選擇口默認實現,將在本章深入講述這個類。除此之外,也將探討action 是如何通過使用JavaBean 屬性提供輸入和輸出的,最后將介紹如何處理文件上傳。
5.1? 實現Aciton 接口
?????????? Struts2 的Action 接口來源于WebWork ,全包名為com.opensymphony.xwork2.Action 如代碼5-10 所示。在Struts2 中定義action 類時已經可以不實現Aciton 接口,Struts2 會以反射的方式來調用action 類。
5.2? 擴展ActionSupport 類
?????????? ActionSupport 是一個讓action 類能夠更快開始工作的基類。它包含了action 能夠提供的許多可選服務的默認實現,讓開發者更容易地開始開發自己的action 類,不需要在為這些可選服務提供具體實現了。同時能夠改寫可選擇接口的任意一個方法實現并保持其他方法的默認實現。由于ActionSupport 預建了許多開箱即用的功能,建議讀者創建自己的action 時都擴展ActionSupport 類。ActionSupport 實現了以下可選擇接口,
5.3? 實現基本校驗
?????????? 通常在執行業務邏輯之前,校驗用戶提供的數據是十分表要的。這種字段校驗包括“某個字段是必須的”、“某個字段必須大于某個值,小于某個值“等內容。為了自動執行校驗,Sturts 2 提供了一種能夠在excuete() 方法被調用之前調用其他方法對action 進行處理的機制,這個機制由com.opensymphony.xwork2.Validateable 接口提供,它包含了一個方法:
?????????? public void validate()
?????????? Validateable 接口為action 增加了一個標記,通過以上方法使得action 能夠自動被校驗。
?????????? 保存和顯示校驗的錯誤信息有接口ValidationAware 來負責,這兩個接口一般會同時使用。
5.4? 使用本地的資源文件
?????????? 本節中將介紹另外兩個接口TextProvider 和LocalProvider ,它們都是為了使用本地的資源文件而設計的。
?????????? 在Java 中用戶語言和地區的信息被封裝在java.util.Local 類中,而action 則通過一個定義與com.opensymphony.xwork.LocaleProvider 接口的方法判斷使用哪個Locale 獲取用于顯示的信息文本,這個接口中只定義了一個方法:
?????????? Public Locale getLocale()
?????????? 在ActionSupport 中,這個方法的默認實現為:通過調用AcitonContext.getContext ().getLocale() 方法,利用ActionContext 獲得locale 的值(關于ActionContext 的使用將在后面的章節詳細描述)。Struts2 通過查詢HttpServletRequest 對象并調用它的getLocale () 方法將Local 與action 調用聯系起來。
5.5? 用ActionContext 與Web 容器發生聯系
?????????? 在Action 的接口定義中,excute() 方法并沒有HttpServletRequest 和HttpServletResponse 參數也就是說Struts2 的Action 不用去依賴于任何Web 容器(不像Struts 1 必須在Web 容器中才能運行),不用與那些JavaServlet 復雜的請求(Request )、響應(Response) 關聯在一起。但在Web 應用程序開發中,往往需要在Action 里直接獲取請求(Request) 或會話(Session )的一些信息,甚至需要直接對JavaServlet Http 的請求、響應操作。Struts2 提供了一個工具,用ActionContext 對象來與Web 容器發生聯系。
?????????? ActionContext (com.opensymphony.xwork.ActionContext )是Action 執行時的上下文,上下文可以把它看作是一個Map ,它存放是Action 在執行時需要用到的對象,比如:上下文放有請求的參數(Parameter )、會話(Session )、Servlet 上下文(ServletContext )、本地化(Locale )信息等。在每次執行Action 之前都會創建新的ActionContext ,ActionContext 是線程安全的,也就是說在同一個線程里ActionContext 里的屬性是唯一的,這樣的Action 就可以在多線程中使用。
5.6? 高級輸入
?????????? 應用程序經常使用JavaBean 表示一個域中的對象,包括User 、Address 、Block 在內地的類就是這種JavaBean 很好的例子。而在Web 程序中很大一部工作都是將信息填充到這些對象中去和從Bean 中獲取數據信息在網頁中表現。本節將以一個完整的實例來說明Sturts 2 在這些方面提供了那些便利。
5.7? 使用Model-Driven
?????????? Struts2 中,提供了兩種Action 驅動模式:Property-Driven (屬性驅動),Model-Driven (模型驅動的)。
?????????? 模型驅動的Action 很像Struts1 中的FormBean ,在傳遞過程中有一個單獨的值對象來作為參數的載體,但在Struts2 中這個值對象不必再繼承任何接口,只要普通JavaBean 就可以充當模型部分。很多情況下Bean 的定義已經存在了,而且是不能修改的(如從外部引入的類或者是已經被大量代碼引用的類),如果必須實現某個接口才能作為FromBean ,不得不再新增一個類,Struts2 的這個改進非常及時。
5.8? 使用Property-Driven
?????????? Property-Driven 就是Action 將直接用自己的字段來充當FormBean 的功能,在Struts2 入門一章中,HelloReader 這個例子就是采用的這種方法,在Action 中直接包含了message 屬性和它set 、get 方法。它一般用在頁面表單比較簡單的情況使用,而且可以直接把屬性作為Action 的字段,這樣就不用在另寫FormBean ,減少了重復代碼。
?????????? 上一節的例子如果使用Property-Driven 方法,那就是將User 與action 類合并定義,把User 中的屬性值直接轉移到action 中去,在配置文件中也不必再增加modelDriven 這個過濾器。
6? Result 類型介紹
?????????? Result 是在Action 執行完,一個結果返回后決定發生什么事的類。開發者可以自由的根據他們的應用和環境的需要創建自己的Result 類型。例如在Struts2 中Servlet 和Velocity 結果類型已經被創建用來顯示web 應用程序的畫面。本節將介紹Struts2 內置的幾種Result 類型和如何自定義開發Result 。
6.1? 內置Result 類型
?????????? 所有的Result 類型都實現了com.opensymphony.xwork.Result 接口。這個接口是所有action 執行結果的通用接口,不管這個結果是用來顯示一個網頁還是產生一個E-mail ,發送一個JMS 消息還是別的。
?????????? 在struts-default.xml 中定義了系統提供的缺省Result 類型,把它們映射為action 配置中可以引用的名字,在action 配置就就不用再使用長類名直接使用這些別名就可以了。
6.2? 默認Result
?????????? Dispatcher Result 是最常用的一種result ,它也是Struts2 默認的result ,又稱為通用resut 。action 執行完后,請求會導向對應的View ,相當于 標簽實現的跳轉功能。將同一個HTTP 請求中的內容分發至某一個頁面(dispatcher 類型的result 的使用) 只要配置文件包含了struts-default.xml ,而且package 繼承了struts-default ,那么使用dispatcher result 并不需要其他設置。示例:
<result name="success" type="dispatcher">
<param name="location">foo.jsp</param>
</result>
6.3? 頁面跳轉 Result
?????????? Redirect Result 與Dispatcher Result 作用類似也是實現頁面跳轉。對上次的響應將重定向到指定的位置,可以理解為在客戶端跳轉用戶又重新請求了一個新的URL 。redirect 是重新產生一個新的request ,因此原來request 保存的東西將不再有效,比如不能通過再requet.getAtrribute() 取得對象,也不能取得action 的實例、errors 、field errors 等。
?????????? Redirect Result 與Dispatcher Result 的區別于源于JSP 篇中 標簽與response.redeiret() 的區別。
6.4? 創建action 鏈
?????????? Chain Result 是一種result 類型,它基于自己的攔截器stack (堆棧)和result 調用一個action ,這樣允許一個action 附帶著原來的狀態將請求轉到目標action
?????????? Struts2 提供把多個Action 按照預先定義好的順序或者流程鏈接起來的能力。這個特性通過給指定的Action 設置一個Chain Result ,然后通過一個ChainingInterceptor 攔截目標Action 來實現。
6.5? 整合各種View 技術
?????????? Velocity 、Freemarker 、JasperReports 、xslt 這4 種result 都是為了整合不同的視圖技術而設計的。
?????????? 1 .Velocity Result:Velocity 是一個基于java 的模板引擎(template engine )。
?????????? 2 .Freemarker Result:Freemarker 也是一個模板引擎,允許JavaServlet 保持圖形設計同應用程序邏輯的分離,這是通過在模板中密封HTML 完成的。模板用servlet 提供的數據動態地生成 HTML 。
?????????? 3 .JasperReports result:JasperReports 是一個基于Java 的開源報表工具,它可以在Java 環境下像其他IDE 報表工具一樣來制作報表。
?????????? 4 .XSLT Result:XSLT Result 用XSLT 來轉換action 對象到XML 。
6.6? 自定義result
?????????? Struts2 也允許用戶自定義自己的result 類型,只要實現com.opensymphony.xwork2.Result 接口就可以了。如代碼5-29 所示,模擬了一種result 作用是根據處理結果將給指用戶發送一份E-mail 。這個result 需要4 個參數to 、from 、subject 和body
7? 攔截器(Interceptors )介紹
?????????? 攔截器(Interceptor )是Struts2 的一個強有力的工具,有許多功能都是構建于它之上,如國際化、轉換器,校驗等。Interceptor 是Struts2 的一大特色,在執行action 之前和之后可以使請求通過一個或多個Interceptor 。多個連接器組合在一起實現某一個功能稱為interceptor 鏈(Interceptor Chain ,在Struts2 中稱為攔截器棧Interceptor Stack )。interceptor 鏈就是將interceptor 按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,interceptor 鏈中的interceptor 就會按其之前定義的順序被調用。
7.1? Interceptor 的原理
?????????? Struts2 的interceptor 實現相對簡單。當請求到達Struts2 的ServletDispatcher 時,Struts2 會查找配置文件,并根據其配置實例化相對的interceptor 對象,然后串成一個列表(list ),最后一個一個地調用列表中的攔截器,
7.2? 內置攔截器介紹
?????????? Struts2 包含了許多內置的interceptor ,它們提供了很多核心功能和可選的高級特性。interceptor 在struts.default.xml 文件中被定義,而一些默認的interceptor 棧及interceptor 的命名也被定義其中。框架中提供了很多實用的Interceptor ,可以隨時使用它們的名字來調用這些interceptor ,
7.3? 使用內置interceptor
?????????? 本節將介紹幾種常用interceptor 的用法:
?????????? 1 .使用timer 為action 即時
?????????? 2 .使用logger 為aciton 提供日志
?????????? 3 .使用校驗
?????????? 4 .準備action
?????????? 5 .實現ModelDriven
?????????? 6 .token 和token-session
7.4? 內置攔截器棧介紹
?????????? 除了內置的interceptor 之外,struts.xml 還包含了內置的interceptor 組合,可以通過具體的命名的interceptor 棧來使用它們。
7.5? 自定義攔截器
?????????? 自定義一個攔截器需要3 個步驟:
?????????? (1 )自定義一個實現Interceptor 接口的類。
?????????? (2 )在strutx.xml 中注冊上一步中定義的攔截器。
?????????? (3 )在需要使用的Action 中引用上述定義的攔截器,為了方便也可將攔截器定義為默認的攔截器,這樣在不加特殊聲明的情況下所有的Action 都被這個攔截器攔截。
8? 小結
?????????? 本文講述的是Struts2 的核心構成元素及其使用方法,使讀者對Sturts 2 的體系結構有了一個清晰的認識。Struts2 是一個開放的系統,它的很多實現對用戶來說都是透明的,它們在struts-default.xml 中配置,開發人員配置自己的元素在struts.xml 中,這個文件是可以拆分并按用戶需要組織的。Struts2 的核心部分由action 、interceptor 、result3 個主要部分構成,Interceptor 是它最大特色。