文章出處

Action的定義

傳統的MVC框架中,Control層一般都是一個類似與Servlet的一個Java對象。因為從職責上講,Control層需要完成以下的職責:

1. 接收從Web容器傳遞過來的參數,并做恰當的類型轉化

2. 調用邏輯處理

3. 搜集數據,并返回到視圖

而在這個其中的第一步和第三步,都離不開Web容器中的對象的處理。

Struts2中的Action,與其他傳統的MVC框架不同,使用了XWork的Action來構造Control層。讓我們首先來看看Action的接口定義:

Java代碼
  1. /**  
  2.  * All actions may implement this interface, which exposes  
  3.  * the execute() method. However, as of XWork 1.1, this is  
  4.  * not required and is only here to assist users. You are  
  5.  * free to create POJOs that honor the same contract  
  6.  * defined by this interface without actually implementing  
  7.  * the interface.  
  8.  */  
  9. public interface Action {   
  10.   
  11.     /**  
  12.      * Where the logic of the action is executed.  
  13.      *  
  14.      * @return a string representing the logical result of the execution.  
  15.      *         See constants in this interface for a list of standard result values.  
  16.      * @throws Exception thrown if a system level exception occurs.  
  17.      *                   Application level exceptions should be handled by returning  
  18.      *                   an error value, such as Action.ERROR.  
  19.      */  
  20.     public String execute() throws Exception;   
  21. }  


我們只需要實現這個接口,就可以在其中編寫業務邏輯完成我們的功能。

Java代碼
  1. public class Index implements Action {   
  2.   
  3.     private static final long serialVersionUID = -1070481751569420550L;   
  4.   
  5.     /* (non-Javadoc)  
  6.      * @see com.opensymphony.xwork2.Action#execute()  
  7.      */  
  8.     public String execute() throws Exception {   
  9.         // write your logic here   
  10.         return SUCCESS;   
  11.     }   
  12. }  


在這個接口定義中,我們可以明顯看到與傳統的MVC框架之間的區別:Struts2中的Action,并不需要依賴于特定的Web容器。我們看不到類似HttpServletRequest,HttpServletResponse等Web容器相關的對象。

而這一點,也帶來了問題:

提問:Struts2的Action并不帶有任何Web容器相關的對象,Action又是如何工作在Web容器中的呢?

雖然Struts2的Action只是一個非常普通的Java對象,并不具備任何Web容器的特質,但是我們需要把Action放到一個更加大的環境中來看。事實上,Struts2為Action的執行,準備了完整的數據環境和執行環境。而這個執行環境,就保證了Action在Web容器中的順利運行。

在Struts2中,每個Http的請求,會被發送到一個Filter。而這個Filter,就會針對每個請求,創建出一個代碼的執行環境,并在這個基礎上,為每個執行環境配備與之對應的數據環境,這個數據環境中的內容,就來自于Web容器中的一個又一個對象。這樣,就能夠順利調用Action執行代碼而無需擔心它是否運行在Web容器中了。

至于這個執行環境和數據環境到底是什么,我們接下來會詳細講到。

提問:Struts2的Action并不帶有任何Web容器相關的對象,Action中又如何與Web容器進行通信并獲取Web容器的相關對象呢?

剛剛我們提到Struts2會為每個Http的請求建立一個執行環境和數據環境。其中,數據環境就成為了Action獲取Web容器的基礎。所以,當Action需要獲取Web容器的相關對象,需要通過數據環境來進行。


Struts2的Action的這一個重要特性,至少能為我們帶來以下好處:

1. 使得Struts2的Action非常易于測試

如果我們完全不考慮Action的執行環境,僅僅把Action看作一個普通的Java對象,那么我們甚至可以直接new一個Action的對象,通過執行其中的方法完成測試。這樣,我們就不需要任何的Mock,來模擬Web容器的環境。

2. 結合Action的執行環境,使得Struts2在Control這個層次上,能夠定義更加豐富的執行層次

因為Action是一個普通的Java類,而不是一個Servlet類,完全脫離于Web容器,所以我們就能夠更加方便地對Control層進行合理的層次設計,從而抽象出許多公共的邏輯,并將這些邏輯脫離出Action對象本身。事實上,Struts2也正是這么做的,無論是Interceptor,還是Result,其實都是抽象出了Action中公共的邏輯部分,將他們放到了Action的外面,從而更加簡化了Action的開發。

3. 使得Struts2的Action看上去更像一個POJO,從而更加易于管理

Struts2的Action是一個線程安全的對象。而Web容器傳遞過來的參數,也會傳遞到Action中的成員變量中。這樣,Action看上去就更像一個POJO,從而能夠方便的被許多對象容器進行管理。比如說,你可以非常方便得把Action納入到Spring的容器中進行管理。

Action的生命周期

接下來,我們再來看看Struts2中的Action的生命周期:



這張圖來自于Struts2的Reference,我們能夠在圖中看到許多我們不熟悉的名詞,比如ActionProxy,Interceptor等等。這些都是Struts2的Control層的重要元素,也是Struts2的Control層的一個層次化的體現。

上面的這張圖基本上能夠概括了Struts2的整個生命周期。接下來,我們就對Action中的一些重要元素進行簡單的描述。

Action的五大元素

在大概了解了Struts2的Action后,我們來重點研究一下在Struts2的Action周圍,為Action進行服務的一些重要元素,這些元素將涵蓋Action的數據環境,Action的執行環境、Action的調度者、Action的層次結構和Action的執行結果。

ActionContext —— 數據環境

之前我們提到了Struts2的Action并不是一個Servlet,它是脫離了Web容器的。但是對于一個Web框架來說,所有的數據請求(Request)和數據返回(Response)都來源于Web容器,那么Action在執行的時候,如何去獲取這些數據呢?

這個問題的答案就在于,我們需要為每個Action準備一個數據環境,這個數據環境被稱之為:ActionContext。由于Action是應對于一個又一個的URL請求,所以ActionContext應該具備以下的特性:

1. ActionContext應成為Action與Web容器之間的橋梁

2. ActionContext中應該保存有針對某個請求的詳細信息

3. ActionContext應該是一個線程安全的類對象

Interceptor —— 豐富的層次結構

簡單回顧一下上面所提到的Action的職責,我們看到,需要在Action這個層面上完成的事情還不少。而完成這些職責,就需要我們對這些職責進行合理的分類和排序,將他們組織成有序的執行隊列。在Struts2中,使用了一種類似責任鏈的設計模式對這些不同的職責進行分類并串聯起來,從而使得Action層具備了豐富的層次結構。而在這個執行隊列中的每個元素,就被我們稱之為Interceptor,也就是攔截器。

Struts2 Reference 寫道
Interceptors can execute code before and after an Action is invoked.


攔截器是AOP中的概念,它本身是一段代碼,可以通過定義“織入點”,來指定攔截器的代碼在“織入點”的前后執行,從而起到攔截的作用。正如上面Struts2的Reference中講述的,Struts2的Interceptor,其攔截的對象是Action代碼,可以定義在Action代碼之前或者之后執行攔截器的代碼。

如果仔細留意一下Action LifeCycle圖中的Interceptor和Action的部分,我們可以看到,Interceptor一層一層的把Action包了起來。這是一個典型的堆棧結構,在代碼執行的時候,每個Interceptor不僅需要文成它自身的邏輯,還通過遞歸調用負責下一個攔截器或Action的調用。

Struts2 Reference 寫道
Most of the framework's core functionality is implemented as Interceptors. Features like double-submit guards, type conversion, object population, validation, file upload, page preparation, and more, are all implemented with the help of Interceptors. Each and every Interceptor is pluggable, so you can decide exactly which features an Action needs to support.


也正如Struts2的Reference所說,Struts2提供的絕大多數的功能支持,都通過Interceptor來實現,這些Interceptor可以隨意進行配置,并且能夠方便的插入到程序中去運行。

Result —— 執行結果

有執行就必然有執行的結果。在Struts2中,Action的執行結果被抽象成了一個層次。在這個層次中,可以定義任意類型的View層的結構。也就是說,Struts2并不強制View層的表現形式,可以是JSP、Freemarker模板、二進制流輸出等等。

Struts2把執行結果抽象成一個層次,使得你可以不再關注許多視圖整合上面的細節,只需要考慮視圖的類型和數據的準備,這樣,你也可以不必在沉浸在雜亂的構造視圖的代碼中。

ActionProxy —— 執行環境

有了攔截器Interceptor,有了Action本身,也有了Action的執行結果Result,我們就需要一個類似調度器的產品,將這些元素整合起來,進行調度執行。在上面的Action Lifecyle的圖中,我們可以看到,Interceptor、Action和Result都處于ActionProxy中,所以ActionProxy就成為了所有這些元素的執行環境。


既然是執行環境,那么ActionProxy就需要提供Action執行的時候一切所需要的配置、參數等等,當然,也要有進行Action調用的入口。所以讓我們來看一下ActionProxy的接口:

Java代碼
  1. public interface ActionProxy {   
  2.   
  3.     /**  
  4.      * Called after all dependencies are set  
  5.      */  
  6.     void prepare() throws Exception;   
  7.        
  8.     /**  
  9.      * @return the Action instance for this Proxy  
  10.      */  
  11.     Object getAction();   
  12.   
  13.     /**  
  14.      * @return the alias name this ActionProxy is mapped to  
  15.      */  
  16.     String getActionName();   
  17.   
  18.     /**  
  19.      * @return the ActionConfig this ActionProxy is built from  
  20.      */  
  21.     ActionConfig getConfig();   
  22.   
  23.     /**  
  24.      * Sets whether this ActionProxy should also execute the Result after executing the Action  
  25.      *  
  26.      * @param executeResult  
  27.      */  
  28.     void setExecuteResult(boolean executeResult);   
  29.   
  30.     /**  
  31.      * @return the status of whether the ActionProxy is set to execute the Result after the Action is executed  
  32.      */  
  33.     boolean getExecuteResult();   
  34.   
  35.     /**  
  36.      * @return the ActionInvocation associated with this ActionProxy  
  37.      */  
  38.     ActionInvocation getInvocation();   
  39.   
  40.     /**  
  41.      * @return the namespace the ActionConfig for this ActionProxy is mapped to  
  42.      */  
  43.     String getNamespace();   
  44.   
  45.     /**  
  46.      * Execute this ActionProxy. This will set the ActionContext from the ActionInvocation into the ActionContext  
  47.      * ThreadLocal before invoking the ActionInvocation, then set the old ActionContext back into the ThreadLocal.  
  48.      *  
  49.      * @return the result code returned from executing the ActionInvocation  
  50.      * @throws Exception  
  51.      * @see ActionInvocation  
  52.      */  
  53.     String execute() throws Exception;   
  54.   
  55.     /**  
  56.      * Sets the method to execute for the action invocation. If no method is specified, the method provided by  
  57.      * in the action's configuration will be used.  
  58.      *  
  59.      * @param method the string name of the method to invoke  
  60.      */  
  61.     void setMethod(String method);   
  62.   
  63.     /**  
  64.      * Returns the method to execute, or null if no method has been specified (meaning "execute" will be invoked)  
  65.      */  
  66.     String getMethod();   
  67.        
  68. }  


很顯然,在這其中,prepare和execute方法是用作Action調用的入口函數,其他的接口定義都與Action執行時的運行參數和配置有關。

ActionInvocation —— 調度者

在上面的ActionProxy的接口定義中,我們可以看到有一個比較特殊的變量:ActionInvocation比較吸引我們的眼球。從字面上去理解,ActionInvocation就是Action的調用者。事實上也是如此,ActionInvocation在這個Action的執行過程中,負責Interceptor、Action和Result等一系列元素的調度。

在之后的章節中,這個ActionInvocation類也將成為我們解讀Struts2源碼的一個重要入手點。這個類將告訴你,Struts2是如何通過ActionInvocation來實現對Interceptor、Action和Result的合理調度的。

攔截器詳解:http://www.javaeye.com/wiki/struts2/1397-deep-into-struts2-interceptors
Result機制,讓視圖更豐富:http://www.javaeye.com/wiki/struts2/1462-result-in-struts2
WebWork2多模塊解決方法: http://www.javaeye.com/topic/6529
============================================================================================================
Struts2配置詳解
Struts2的配置文件是以XML的形式出現的。不過它的XML的語義比較簡單,下面是我抽取了位于struts2-core-2.0.14.jar內部的struts-default.xml的片段:

Xml代碼
  1. <struts>  
  2.     <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />  
  •     <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />  
  •   
  •     <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>  
  •     <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/>  
  •        
  •     <!-- 省略了其他的bean節點的定義 -->  
  •   
  •     <!--  Only have static injections -->  
  •     <bean class="com.opensymphony.xwork2.ObjectFactory" static="true" />  
  •     <bean class="com.opensymphony.xwork2.util.XWorkConverter" static="true" />  
  •        
  •      <!-- 省略了其他的靜態注入的定義 -->  
  •      
  •     <package name="struts-default" abstract="true">  
  •         <result-types>  
  •             <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>  
  •             <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>  
  •             <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>  
  •   
  •             <!-- 省略了其他的ResultType的定義 -->  
  •     
  •         </result-types>  
  •   
  •         <interceptors>  
  •             <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>  
  •             <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>  
  •   
  •             <!-- 省略了其他的Interceptor的定義 -->  
  •   
  •             <!-- Basic stack -->  
  •             <interceptor-stack name="basicStack">  
  •                 <interceptor-ref name="exception"/>  
  •                 <interceptor-ref name="servletConfig"/>  
  •                 <interceptor-ref name="prepare"/>  
  •                 <interceptor-ref name="checkbox"/>  
  •                 <interceptor-ref name="params"/>  
  •                 <interceptor-ref name="conversionError"/>  
  •             </interceptor-stack>  
  •   
  •             <!-- A complete stack with all the common interceptors in place.   
  •                  Generally, this stack should be the one you use, though it   
  •                  may do more than you need. Also, the ordering can be   
  •                  switched around (ex: if you wish to have your servlet-related   
  •                  objects applied before prepare() is called, you'd need to move   
  •                  servlet-config interceptor up.   
  •   
  •                  This stack also excludes from the normal validation and workflow   
  •                  the method names input, back, and cancel. These typically are   
  •                  associated with requests that should not be validated.   
  •                  -->  
  •             <interceptor-stack name="defaultStack">  
  •                 <interceptor-ref name="exception"/>  
  •                 <interceptor-ref name="alias"/>  
  •                 <interceptor-ref name="servletConfig"/>  
  •                 <interceptor-ref name="prepare"/>  
  •                 <interceptor-ref name="i18n"/>  
  •                 <interceptor-ref name="chain"/>  
  •                 <interceptor-ref name="debugging"/>  
  •                 <interceptor-ref name="profiling"/>  
  •                 <interceptor-ref name="scopedModelDriven"/>  
  •                 <interceptor-ref name="modelDriven"/>  
  •                 <interceptor-ref name="fileUpload"/>  
  •                 <interceptor-ref name="checkbox"/>  
  •                 <interceptor-ref name="staticParams"/>  
  •                 <interceptor-ref name="params">  
  •                   <param name="excludeParams">dojo\..*</param>  
  •                 </interceptor-ref>  
  •                 <interceptor-ref name="conversionError"/>  
  •                 <interceptor-ref name="validation">  
  •                     <param name="excludeMethods">input,back,cancel,browse</param>  
  •                 </interceptor-ref>  
  •                 <interceptor-ref name="workflow">  
  •                     <param name="excludeMethods">input,back,cancel,browse</param>  
  •                 </interceptor-ref>  
  •             </interceptor-stack>  
  •   
  •             <!-- 省略了其他的interceptor-stack節點的定義 -->  
  •   
  •        </interceptors>  
  •   
  •         <default-interceptor-ref name="defaultStack"/>  
  •   
  •     </package>  
  •   
  • </struts>  


  • 在這個配置文件中,我們可以看到,Struts2的XML自身所支持的節點和子節點并不是很多,大致來說,這些節點可以分成基本配置定義Runtime配置定義

    基本配置定義

    基本配置定義,主要是針對在Struts2內部所使用的各種元素的聲明。這些聲明往往規定了Struts2內部的一些行為特征。

    例如,配置文件中的<bean>節點,被用于定義Struts2中所使用的接口和實現類,通過Struts2內部實現的IoC,你就可以在不同的實現類之間進行切換。

    再例如,配置文件中的<result-type>節點和<interceptor>節點。他們用于定義Struts2中所支持的所有的Result類型和攔截器,這些定義和聲明,將在Runtime的配置定義中被引用。

    我之所以把配置文件中的這些節點單獨列出來,作為一個種類,是因為這些節點是不可省略的,也是無法簡化的。所以,如果我們試圖在Struts2中簡化配置,我們就需要在Runtime配置定義中下功夫,而這些基本配置定義,我們可以認為是Runtime配置定義的基礎。

    Runtime配置定義

    Runtime配置定義,主要指的的是對Struts2運行過程中,具體的某個Action的行為的指定。這些指定主要通過<package>節點中的<action>節點來完成。

    仔細翻閱<action>節點,我們可以發現,它是URL與Action之間溝通的橋梁,也就是說,它定義了URL與Action之間的對應關系。同時,它還指定了Action在執行過程中的具體行為,包括Action執行的時候使用什么樣的攔截器、Action執行完畢后,轉向到什么樣的Result等等。

    Runtime配置定義是可以簡化的,Struts2中提供了很多種簡化配置的方式,這個在之后的文章中會詳細提到。

    模塊化管理配置文件

    一旦項目變得很大,項目中同時也并不采取什么簡化配置的措施,那么在默認情況下,配置文件就會變得很大而不易于維護。這個時候,對于配置文件的模塊化管理的需求就顯現出來。Struts2提供了兩種方式對配置文件進行模塊化管理。

    plugin機制

    Struts2有plugin的機制,有關plugin的具體的知識,請參考我的另外一篇專欄文章:《深入plugin》 ——  http://www.javaeye.com/wiki/struts2/1333-deep-into-plugin。在這里,我也就不詳細介紹了。

    在每個plugin中,都會有一個叫做struts-plugin.xml的配置文件,這個配置文件的格式與struts-default.xml的格式是相同的。可以在其中做出任何的Struts2的定義和配置。我們知道,Struts2的配置文件的加載順序,是按照以下的順序來:

    Struts2 Referece 寫道
    1. struts-default.xml (bundled in the Core JAR)
    2. struts-plugin.xml (as many as can be found in other JARs)
    3. struts.xml (provided by your application)


    所以,struts-plugin.xml中的配置的效果實際上與struts-default.xml的效果是相同的。這樣,通過各種各樣不同的plugin,就等于將Struts2的配置,按照plugin的功能不同而分開了。從而起到了配置文件模塊化管理的效果。

    使用include節點

    plugin中的配置文件,實際上是位于classpath的JAR包中的,那么我們在項目中,如何對一個龐大的配置文件進行拆分呢?在Struts2中,可以使用include節點對所有的Struts2配置文件進行拆分和模塊化管理。例如:
    Xml代碼
    1. <struts>  
    2.   
    3.     <include file="struts-default.xml"/>  
    4.     <include file="web/struts-config.xml"/>  
    5.     <include file="web/struts-action.xml"/>  
    6.        
    7. </struts>  

    其中,file所指定的文件是相對于classpath的相對目錄中的文件。而每個配置文件的格式與struts-default.xml的格式也是相同的。

    通過include節點,我們就可以對一個比較大的配置文件按照功能和模塊進行拆分,這在一個大型的團隊開發中,是相當有意義的。

    簡單的IoC

    在基本配置定義中,有兩個很常用的節點:<bean>和<constant>。在系統啟動的時候,Struts2會根據配置文件中這些<bean>和<constant>節點的定義進行加載,并初始化成為Struts2的默認行為。這種初始化的行為,非常類似于Spring中的依賴注入(IoC),從而使得你不再需要擔心這些對象在運行時的創建和銷毀,所有的工作都由Struts2內部的機制實現。接下來我們就來看看Struts2是如何實現IoC的。

    Struts2 Reference 寫道
    Internally, the framework uses its own dependency injection container that is very similar to Google Guice (both were originally developed by Bob Lee)


    這是來自于Struts2的Reference對它自身的IoC的描述。如果熟悉Guice的朋友一定知道,Guice的實現使用了Annotation的方式進行,而整個依賴注入的實現,是通過一個內部的容器類進行的。Struts2的依賴注入,與Guice的機制完全一致。根據注入的內容的不同,Struts2的IoC可以對容器中的對象的依賴關系進行管理,也可以注入一些靜態變量。

    bean注入

    對于bean的注入,對應于XML中的bean的節點聲明。我把其中的機制分成了3個部分:

    1. 容器中對象的聲明

    Xml代碼
    1. <bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />  
    2. <bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />  


    這點沒什么好說的,在struts.xml中,你可以為某個接口聲明它所對應的實現類。

    name屬性

    你可以聲明多個實現類,使用name屬性進行區分。在注入的時候,將使用這個屬性的值作為接口實現類的選擇。

    required屬性

    你還可以通過required屬性,來指定是否在運行時必不可少的注入。如果reqired被設置成false,那么當不存在相應的接口定義時,注入將被忽略。

    static屬性

    在XML的定義中,還可以使用static屬性。如果static屬性被設置成true,那么注入將針對bean中的static方法和static屬性進行。

    2. 在代碼中使用Annotation進行注入

    Java代碼
    1. @Inject("xwork")   
    2. protected ObjectFactory objectFactory;   
    3.   
    4. public LightURLUnknownHandler(@Inject ObjectFactory objectFactory) {   
    5.     this.objectFactory = objectFactory;   
    6. }   
    7.   
    8. @Inject  
    9. public void setObjectFactory(ObjectFactory factory) {   
    10.     this.objectFactory = factory;   
    11. }   
    12.   
    13. @Inject(required=false)   
    14. public void setUnknownHandler(UnknownHandler handler) {   
    15.     this.unknownHandler = handler;   
    16. }  


    在代碼中,使用@Inject這樣一個Annotation進行對象依賴注入。在上面的例子中,我們可以看到,@Inject這個Annotation,可以作用在屬性上,也可以作用在方法上,甚至可以作用在方法的參數上。

    在默認情況下,如果@Inject不指定value,那么XML配置定義中的name="default"或者name=""的實現類定義將被注入。

    那么,在struts-default.xml中,Struts2到底選擇了那些實現類,作為Struts2或者XWork內部接口的默認實現類呢?默認情況下,struts-default.xml中定義的bean的name="struts"的將被作為默認的接口實現類被注入。這些默認行為,是由org.apache.struts2.config.BeanSelectionProvider所決定的,有興趣的讀者可以參閱這個類的源碼。

    3. 內部的Container機制完成一切背后工作

    上面看到的,是現象。在內部,Struts2通過一個Container來實現所有的注入機制。

    Java代碼
    1. public interface Container extends Serializable {   
    2.   
    3.   /**  
    4.    * Default dependency name.  
    5.    */  
    6.   String DEFAULT_NAME = "default";   
    7.   
    8.   /**  
    9.    * Injects dependencies into the fields and methods of an existing object.  
    10.    */  
    11.   void inject(Object o);   
    12.   
    13.   /**  
    14.    * Creates and injects a new instance of type {@code implementation}.  
    15.    */  
    16.   <T> T inject(Class<T> implementation);   
    17.   
    18.   /**  
    19.    * Gets an instance of the given dependency which was declared in  
    20.    * {@link com.opensymphony.xwork2.inject.ContainerBuilder}.  
    21.    */  
    22.   <T> T getInstance(Class<T> type, String name);   
    23.   
    24.   /**  
    25.    * Convenience method.&nbsp;Equivalent to {@code getInstance(type,  
    26.    * DEFAULT_NAME)}.  
    27.    */  
    28.   <T> T getInstance(Class<T> type);   
    29.      
    30.   /**  
    31.    * Gets a set of all registered names for the given type  
    32.    * @param type The instance type  
    33.    * @return A set of registered names  
    34.    */  
    35.   Set<String> getInstanceNames(Class<?> type);   
    36.   
    37.   /**  
    38.    * Sets the scope strategy for the current thread.  
    39.    */  
    40.   void setScopeStrategy(Scope.Strategy scopeStrategy);   
    41.   
    42.   /**  
    43.    * Removes the scope strategy for the current thread.  
    44.    */  
    45.   void removeScopeStrategy();   
    46. }  


    在系統啟動的時候,這個Container的實現類就會工作,把XML中定義的內容進行注入。有興趣的讀者可以繼續探尋這個接口的實現類:com.opensymphony.xwork2.inject.ContainerImpl。

    靜態變量(Constant)的注入

    @Inject這個Annotation不僅能夠對接口的實現類進行注入,也能夠對靜態變量進行注入。

    有關靜態變量的聲明和注入,在我的另外一篇專欄文章中已經詳細闡述:《深入plugin》 ——  http://www.javaeye.com/wiki/struts2/1333-deep-into-plugin。在這里,我也就不詳細介紹了。

    package節點詳解

    package節點是整個配置的核心部分。每個package,從語義上講,其實代表了每一個獨立的模塊。在這個模塊中,你可以定義隸屬于這個模塊的行為方式,而與其他的模塊沒有關系。所以,每個package都有獨立的interceptor、result-type和action的定義,絕大多數的Runtime配置定義都是通過package節點實現的。接下來我們就來詳細討論一下package中的屬性和子節點。

    基本屬性

    1. name

    name屬性為每個package設置一個唯一的標識,這個標識在所有的package定義中不能重復。

    2. abstract

    標識這個package的定義是一個抽象定義,也就是允許他僅包含聲明式的定義,而不需要在package定義中包含action的定義。

    3. extends

    通過使用extends,你可以指定本package繼承另外一個package的所有的配置。當某個package繼承了另外一個package的所有配置,那么你就無需對父package中已經聲明過的配置定義做再次的定義。

    同時,如果重復定義父package中已聲明過的配置定義,那么這些重復定義聲明將覆蓋父package中的相關定義。

    4. namespace

    Struts2 Reference 寫道
    The namespace attribute subdivides action configurations into logical modules, each with its own identifying prefix. Namespaces avoid conflicts between action names. Each namespace can have its own "menu" or "help" action, each with its own implementation.


    這段來自Struts2的Reference的引用,基本上闡明了namespace的作用:對于action配置進行邏輯劃分。

    如果我們不為package節點指定namespace,Struts2默認使用一個空字符串作為默認的namespace。當然,也可以使用"/"等字符串來表示namespace。

    Struts2在根據URL進行尋址的時候,使用以下的步驟:

    1) 根據URL進行Namespace和ActionName的計算

    2) 根據計算的得到的Namespace和ActionName查找package節點中相應配置

    3) 如果查找失敗,則查找Namespace為空,ActionName為整個URL的配置

    有關上述3點的詳細信息,請參考Struts2的Reference:http://struts.apache.org/2.0.14/docs/namespace-configuration.html

    result-types節點

    在result-types節點中,我們可以聲明在本package中所支持的Result類型。這些Result類型,將在action節點中被引用到。

    interceptors節點

    在interceptors節點中有兩類節點:<interceptor>和<interceptor-stack>。這兩個節點都用于聲明攔截器。前者的作用,是真正定義一個攔截器。而后者則通過引用已經定義的攔截器,指定他們的執行順序。

    當我們在試圖在Action中引用攔截器時,我們實際上是為某個Action指定需要執行哪些攔截器,并且為這些攔截器指定執行順序。所以Action所引用的,是某個<interceptor-stack>中的定義。

    缺省配置指向

    為了簡化配置,我們可以在package節點中指定本package內的缺省配置指向。這可以通過<default-interceptor-ref>、<default-action-ref>、<global-results>等子節點來完成。

    action節點

    action節點是所有的Runtime配置的核心內容。它的主要作用就是指定URL與Action之間的映射關系。同時,在action節點中你也可以指定action執行時的相關配置,例如action所引用的interceptor等。

    參考文檔

    上面所有的內容,實際上我只是做了一些簡單的概括和歸納,至于每個節點語義和每個節點中具體屬性的使用,我認為還是需要參考Struts2的Reference,因為Reference的講解比任何教程都來的詳細和正確,所以希望大家在了解了這些配置的基本分類之后,重新閱讀Struts2的Reference的相關章節,從而更加深刻的理解Struts2配置文件的方方面面:http://struts.apache.org/2.0.14/docs/configuration-elements.html
    posted @ 2009-07-23 14:54 蟋蟀的草坪 閱讀(...) 評論(...) 編輯 收藏

    不含病毒。www.avast.com
    arrow
    arrow
      全站熱搜
      創作者介紹
      創作者 AutoPoster 的頭像
      AutoPoster

      互聯網 - 大數據

      AutoPoster 發表在 痞客邦 留言(0) 人氣()