Struts2詳講

一 概述

1.什麼是Struts2?

Struts2是一個在WebWork框架基礎上發展起來開源MVC框架。html

2.StrutsPrepareAndExecuteFilter

StrutsPrepareAndExecuteFilter本質上是一個過濾器,配置在web容器中,該過濾器做爲Struts框架的啓動項,將符合擴展名要求的請求轉發給Struts框架處理,將處理結果返回給瀏覽器。java

對於那些符合擴展名要求的請求,StrutsPrepareAndExecuteFilter直接將其轉發給Struts框架,沒有沿着過濾器鏈向下傳遞,因此配置在其後的過濾器不起做用。那些不符合擴展名要求的請求將沿過濾器鏈正常執行。web

3.Struts2的基本結構與執行過程

StrutsPrepareAndExecuteFilter(爲便於表述,如下稱做struts啓動項)將符合擴展名要求的請求轉發到Struts框架中,首先生成一個ActionMapping,該ActionMapping表明了須要調用的Action對象,基於ActionMapping返回一個ActionProxy,proxy經過配置管理器查詢配置文件struts.xml,返回一個ActionInvocation對象,由Invocation對象具體調用action中的方法。在action方法調用前以及調用後都會執行一系列攔截器,最後將響應結果反饋給瀏覽器。數組

4.Action

⑴角色地位

Action是Struts2框架的控制器,將請求分發給響應的業務邏輯。每請求一次,建立一個對象。瀏覽器

⑵繼承關係

建立Action對象時推薦繼承ActionSupport,由於ActionSupport實現了不少接口,繼承該類能夠擁有國際化、數據驗證等功能。Struts2也支持不繼承任何類的POJO對象,只能實現一些簡單的功能,如表單注入,實現類型轉化、數據驗證、攔截器、國際化、文件上傳等特殊功能時必須繼承ActionSupport。安全

⑶動態Action

在請求url中加入方法名以訪問Action中指定方法的機制。
高版本的Struts禁用了動態Action,可使用通配符,動態地調用action中的方法:服務器

  • url:actionName_method;
  • 配置文件:<action name="action_*"method="{1}">;

⑷數據存儲

  • 屬性保存在值棧中。
  • 成員變量保存在context區域的action對象中,訪問方式#action.name。

5.struts.xml文件

struts.xml是Action對象的默認配置文件。
格式:session

<struts>
   <package name="resultList"extends="struts-default"abstract="true">
     <global-results>
        <result name="">/xxx.jsp</result>
     </global-results>
   </package>

  <include file="newFile.xml"></include>
  <contant name="struts.devMode"value="true"></constant>
  <package name="packageName"extends="struts-default"> 
     <global-results>
        <result name="">xxxx</result>
     </global-results>

     <action name="url*"class="完整包名{1}">
        <result name="res" type="">/xxx.jsp</result> 
        <result name=""type="redirect">
           <param name="location">/xxx.jsp</parame>
           <param name="key">value</param>
        </result>
        <result name=""type="redirectAction">
           <param name="actionName">actionName</param>
           <param name="key">value</param>
        </result>
    </action>
  </package>    
</struts>

標籤說明:數據結構

⑴<include>

爲不一樣的模塊設定不一樣的配置文件,最後集中到默認的配置文件struts.xml中。app

⑵<constant>

設定struts框架的屬性。

⑶<package>

  • name:指定包名,以便引用此包。
  • extends:經過包名繼承其餘包。因爲配置文件從上向下讀取,父包必須放在子包上方。因爲"struts.default"包定義了action對象的基本信息,任何一個包都必須直接或者間接地繼承該包。
  • namespace:命名空間。Struts容許不一樣包中的action擁有相同的名稱,爲了區分相同名稱的action,爲action所在的包添加一個屬性,即namespace,格式「/xxx」,訪問此包下的action須要經過此路徑在沒有使用名稱空間的時候,action對象的路徑默認爲「/name」,訪問時能夠直接使用名稱name,如<a href="name">。一旦設定了名稱空間路徑變成「namespace/name」,訪問時必須使用該路徑。

⑷<action>

  • name:設定訪問action對象的url。
  • class:action對象的類名,加載時使用,未指定,默認加載ActionSupport類,該類execute方法的返回值爲success。
  • method:設定請求action對象時,調用Action對象中的哪個方法,默認是execute方法。
  • converter:指定此Action對象使用的類型轉換器。
  • <global-results>:若是包內的Action對象返回相同的視圖,能夠爲這些Action對象統一設定視圖。該設定在整個包範圍內有效,也能夠在包外定義,即應用範圍的全局視圖。

⑸<result>

name:設定一個名稱,當action對象返回的字符串與該名稱值相同時,返回對應的文件(如jsp、html)給瀏覽器,name默認值爲success。返回文件採用以「/」開頭相對於項目的路徑形式。
type:設定頁面跳轉的類型:

  • dispatcher:將請求轉向一個視圖。
  • redirect:重定向到一個視圖。
  • chain:請請求轉發給另外一個Action。
  • redirectAction:重定向到另外一個Action。

二 表單注入

1.分散注入

在Action中定義與表單輸入同名的屬性,底層自動調用setter方法將輸入注入到屬性中。

2.DomainModel 

建立一個封裝了表單輸入的類,在Action中引用該類的對象,併爲對象提供getter/setter方法。要求表單輸入必須指明所屬的對象,實現方式將表單輸入的名稱定義爲「obj.name」,obj是Action中封裝對象的引用變量。

底層實現過程:域模型爲表單輸入指明瞭所屬的對象,Action對象建立完成之後,調用getter方法,若是存在對象,則返回,不存在,
接着調用setter方法建立對象,對象建立完成之後,調用對象中的setter方法爲對象屬性賦值。

若是表單輸入能夠被封裝到一個類的多個對象中,那麼可使用域模型將輸入注入到向Action對象的List集合中,實現以下:
在表單輸入中使用List集合引用變量與下標給表單輸出指定所屬的對象:

<input type="text"name="list[0].name">

3.ModelDriven

Action對象實現ModelDriven接口中的getModel方法,getModel方法負責接收表單輸入,將輸入注入到實體對象中,並返回實體對象不須要在表單輸入中指明將要注入的對象,即表單輸入名稱前再也不加對象的引用變量。

三 與Servlet API交互

1.獲取Map類型的交互對象

Struts2提供了Map類型的request、session、application,經過ActionContext對象獲取,首先獲取ActionContext對象:

ActionContext context = ActionContext.getContext();

獲取request、session、application對象的通用方法,以request爲例:

Map<Object Object> request=context.get("request");

獲取session對象的專有方法:

Map<Object,Object> session=context.getSession();

獲取application對象的專有方法:

Map<Object,Object> application=context.getApplication();

2.獲取直接對象

Struts提供了一個ServletActionContext類,利用該類的靜態方法能夠獲取request\session\application對象:

HttpServletRequest request=ServletActionContext.getRequest();
HttpSession session=request.getSession();
ServletContext servletContext=ServletActionContext.getServletContext();

四 ActionContext 

1.單例

一個線程,一個ActionContext;一個Action,一個ActionContext,ActionContext是線程安全的。

2.結構

3.值棧

對應於OGNL的根,使用OGNL表達式能夠直接獲取其中的數據。數據結構:

⑴獲取值棧

ActionContext context = ActionContext.getContext();
ValueStack valueStack = context.getValueStack();

⑵向值棧中添加數據

valueStack.push(Object obj);//將數據放到棧頂
valueStack.set(Object key,Object value);//若是頂層存在Map集合,則放入Map集合中;不存在,建立一個,再將數據放入其中

⑶獲取數據

valueStack.peek();// 獲取棧頂元素
valueStack.pop();// 獲取並刪除該棧頂元素 

若是棧中的對象包含多個屬性,獲取屬性值時不須要經過對象引用的形式,能夠直接使用屬性名,系統從棧頂開始檢索,找到第一個屬性名稱相同的屬性,將其值返回。
4.request、session、application(Map類型的集合):在OGNL中,採用#request.key獲取屬性值。
5.parameters:存放請求參數,經過#parameters.name獲取請求參數的值。
6.attr:不指定屬性的範圍,能夠經過#attr.key獲取屬性的值,檢索順序request、session、application。
7.action對象:獲取其中的數據<s:property value="#action.varName"/>。

五 OGNL

1.OGNL是什麼?

Object Graph Navigation Language,能夠自動導航對象的結構,訪問對象,爲對象賦值。

  • 自動導航對象結構:訪問對象中的變量時不須要經過對象引用,僅有變量名便可,OGNL將自動檢索對象,找到變量。
  • OGNL自己不具備向頁面輸出的能力,須要與Struts標籤配合才能向頁面輸出內容。

2.OGNL的結構層次

OGNL的核心是OGNL上下文,OGNL上下文至關於一個Map類型的容器,能夠存儲任何類型的數據。OGNL分層次,有一個區域是OGNL的根,能夠直接訪問根中數據,訪問其餘區域的數據須要在前面加「#」。

3.OGNL的做用

  • 獲取OGNL上下文中的數據。
  • 爲變量賦值。
  • 操做集合:訪問集合中的數據,判讀集合是否爲空,獲取集合的長度。
  • 訪問對象的方法。
  • 訪問靜態方法。

4.OGNL的使用範圍

  • OGNL是爲Struts框架開發的,只能用於從Action對象中獲取數據。
  • OGNL上下文對應於Struts的ActionContext,用於從ActionContext中獲取數據。
  • OGNL表達式語言與EL、JSTL等各類表達式語言、標籤庫同樣只能用在JSP中,減小java代碼,不能用在HTML中。

5.OGNL的使用

⑴${xxx}

  • 這種與EL表達式相同的形式並非EL表達式,而是OGNL表達式。
  • 既能夠訪問值棧中的數據,也能夠訪問valueContext除parameters之外的數據。
  • 可用在struts.xml文件中。
  • 只能直接訪問對象,不能在對象前加做用域,好比request、action等,不能加#。
  • 訪問優先級request>valueStack>context>session>application。

⑵%{xxx}

當指定內容有可能被執行環境當作字符串時,告知指定內容是OGNL表達式。

⑶訪問靜態屬性與方法

@完整類名@屬性名::訪問靜態變量。
@完整類名@method():訪問靜態方法。

Struts默認狀況下不容許訪問靜態方法,如需訪問須要在配置文件中作以下設置:

<constant name="struts.ognl.allowStaticMethodAccess"value="true"/>

訪問java.lang.Math中的靜態方法,能夠由@java.lang.Math@method()簡寫爲@@method()。

⑷訪問數組

arr[index]:獲取數組指定索引位置的元素。
arr.length:獲取數組的長度。

⑸訪問List、Map集合

  • list[index]:List集合有序,能夠按照操做數組的方式經過索引訪問數據。
  • map.key:經過指定的key獲取value。
  • map.keys:以數組形式返回map集合中的所有key。
  • map.values:以數組的形式返回map集合中的所有value。
  • list/map.isEmpty:判斷集合是否爲空。
  • list/map.size():獲取集合長度。

⑹投影

  • 做用:獲取集合的某一列,返回一個集合。
  • 操做方式:list.{attr02}(針對值棧中的對象)。

⑺選擇

獲取集合中知足條件的對象,針對行的操做,返回一個集合。
操做方法:

  • list.{?#this.attr>value}:獲取知足條件的所有元素。
  • list.{^#this.attr>value}:獲取知足條件的第一條元素。
  • list.{$#this.attr>value}:獲取知足條件的最後一個元素。

六 Interceptor

1.繼承關係

建立攔截器對象時,若是直接實現Interceptor接口,需實現其中的三個方法,而這三個方法在實際中並非所有須要的。AbstractInterceptor是一個抽象類,實現了Interceptor接口,實現了init、destroy方法,所以自定義攔截器對象時,繼承該方法,只實現intercept方法便可。

2.分離原則

攔截器對象的生命與引用分離,先聲明,後引用。

3.顯式引用問題

在不作其餘特殊設定的狀況下,使用了自定義的攔截器,系統默認的攔截器失效。因爲系統默認的攔截器比較重要,實現重要功能不可缺乏,所以應該將系統默認的攔截器包含進去,而且放在其餘自定義攔截器前面。

4.攔截器配的定義

⑴分散式

分別定義與引用多個攔截器。

----------------攔截器聲明------------------------
<interceptors>
     <interceptor name="interceptor01"class=""/>
     <interceptor name="interceptor02"class=""/>
</interceptors>
----------------攔截器引用------------------------
<action>
     <interceptor-ref name="defaultStack"/>
     <interceptor-ref name="interceptor01"/>
     <interceptor-fef name="interceptor01"/>
</action>

⑵集中式

將多個攔截器集中在攔截器棧中,引用攔截器棧。攔截器棧至關於攔截器集合。

----------------攔截器聲明------------------------
<interceptors>
    <interceptor name="interceptor01"class=""/>
    <interceptor name="interceptor02"class=""/>
    <interceptor-stack name="myStack">
       <interceptor-ref name="defaultStack"/>
       <interceptor-ref name="interceptor01"/>
       <interceptor-fef name="interceptor01"/>
    </interceptor-stack>
</interceptors>
----------------攔截器引用------------------------
<action>
     <interceptor-ref name="myStack"/>
</action>

⑶默認式

將攔截器集中在默認攔截器中,對包中全部的Action有效。

----------------攔截器聲明------------------------
<interceptors>
    <interceptor name="interceptor01"class=""/>
    <interceptor name="interceptor02"class=""/>
    <interceptor-stack name="myStack">
        <interceptor-ref name="defaultStack"/>
        <interceptor-ref name="interceptor01"/>
        <interceptor-fef name="interceptor02"/>
    </interceptor-stack>
</interceptors>
<default-interceptor-ref name="myStack"/>

5.MethodFilterInterceptor

直接繼承AbstractInterceptor建立的攔截器對全部方法都進行攔截,繼承MethodFilterInterceptor建立的攔截器能夠指定攔截的方法。
引用方式:

<interceptor-ref name="interceptor01"class="">
     <param name="includeMethods">methodName01,methodName02</param>//指定攔截的方法
     <param name="excludeMethods">methodName01,methodName02</param>//指定不攔截的方法    
</interceptor-ref>

七 類型轉換器

1.內置類型轉換器

Struts內置了許多類型轉化器,主要是將字符串轉化爲基本數據類型及其包裝類,將字符串轉化爲日期。系統默認的日期格式是yyyy-MM-dd HH:mm:ss,當輸入的字符串符合該格式時,自動轉化爲日期。

2.自定類型轉換器建立

public class MyDateConverter extends DefaultTypeConverter {

    @Override
    public Object convertValue(Object value, Class toType) {
        // TODO Auto-generated method stub
        return super.convertValue(value, toType);
    }

}

該方法分爲兩部分,有時執行兩次:

  • 判斷輸入格式是否知足要求,知足,將格式類型存到ActionContext中;不知足,拋出TypeConversionException。
  • 將轉換後的數據還原,回顯到瀏覽器。

3.TypeConversionException

發生類型轉換異常,會返回值input,須要在配置文件中定義input對應的返回頁面。input只在拋出TypeConversionException時才返回,所以須要在自定義類型轉換器中拋出該異常,能夠先判斷表單輸入的格式是否是容許的格式,若是不是容許格式,拋出該異常。

4.註冊

⑴局部類型轉換器

文件命名方式ActionClassName-conversion.properties,放在Action包中,屬性文件內容:

屬性名=類型轉換器全限定性類名

⑵全局類型轉換器

文件命名方式xwork-conversion.properties,放在src目錄下,屬性文件內容:

屬性類型(如java.util.Date)=類型轉換器全限定性類名

5.回顯提示信息

修改類型驗證返回的提示信息,建立ActionClassName.properties文件,放在Action包中,內容:

invalid.fieldvalue.fieldname=數據驗證失敗提示信息

八 數據驗證機制

1.手動驗證器

ActionSupport實現了Validatable接口,對其中的方法validate只是空實現,所以手動編寫驗證器時,繼承ActionSupport類,重寫validate方法。手動驗證器默認對對象內的全部方法都進行驗證。

public class MyValidator extends ActionSupport{
    public void validate(){
       if(xxxxxx)//驗證若是未經過
          this.addFieldError("字段名","顯示的錯誤提示信息");
    }
}
  • 手動驗證默認對Action對象中的所有方法有效,若是須要只對某個方法有效,能夠修改validate方法的方法名,修改成validate+方法名(首字母大寫)。
  • 自定義驗證器不支持數據回顯。

2.文件驗證

驗證文件的命名規則:

ActionClassName-validation.xml:對Action對象中全部方法都進行驗證。
ActionClassName-AliasName(配置中的名稱)-validation.xml:只對指定的方法進行驗證。

驗證文件的編寫:

<validators>
    <field name="fieldName">
        <field-validator type="requiredstring">
            <message>驗證未經過時輸出的信息</message>
        </field-validator>
    </field>
<validators>

驗證未經過時,Action返回值「input」,所以須要在<action>標籤內部配置返回值爲「input」時的返回頁面。

3.優先級

若是爲同一個方法同時定義了多個數據驗證方式,執行的優先級:基於XML的針對方法的驗證>基於XML的針對對象的驗證>手動編寫的針對方法的驗證>手動編寫的針對對象的驗證

九 Struts標籤庫

引用:

<%@taglib prefix="s" uri="/struts-tags"%>

主要標籤:
1.<s:debug/>:在頁面中顯示ActionContext中的內容。
2.<s:property value=""/>:

  • 向頁面輸出內容,或者取值傳遞。
  • 不管是否含有「#」,都首先從值棧中尋找數據,未找到,再到context中尋找。
  • <s:property/>:未明確賦值的狀況下,從值棧棧頂取值。

3.<s:property value="#request.name"/>:

  • 先從request中搜索,再從值棧中搜索,最後從context搜索。
  • 爲了保證請求結束之後,值棧以及context所佔內容清空,值棧與context與request創建起了聯繫,使得經過request能夠訪問值棧與context中的數據。

4.<s:set name=""value="''"scope=""/>:賦值語句,字符串必須在內層使用‘',外層使用"",未指定做用域時放在context。
5.<s:iterator value=""/>:將遍歷對象放到值棧棧頂。
6.<s:property value="'s' in #list/>:判斷某個數據是否在列表中,相應地,還有不存在判斷not in。
7.<s:push value=""/>:將數據壓入棧中,以便之後訪問,該標籤不穩定,減小使用。 

十 防止表單重複提交

1.令牌機制

首次訪問表單頁面,若是該頁面採用了令牌機制,token攔截器生成一個隨機數,將這個隨機數在瀏覽器與服務器端各保存一份。首次提交表單時,token攔截器對比兩個參數,相同,完成提交,而後服務器單方面修改該隨機數,不通知瀏覽器。瀏覽器再次提交,兩個參數值不相同,token攔截器返回invalid.token值,跳轉到該值對應的頁面,請求未到達Action對象,這樣就防止了重複提交。

2.令牌機制實現

  • 必須在表單內部插入<s:token/>,以存放服務器端生成的隨機數,底層生成了一個name爲token的隱藏域。(struts表單默認以post方式提交)
  • 必須使用系統建立的token攔截器,因爲該攔截器不在默認的攔截器之列,須要手動添加。

十一 國際化

1.什麼是國家化?

使同一個事物適應多種國際環境,就叫作國際化,在Web應用程序開發中就是使頁面可以按照多種語言來顯示。

2.Struts實現國際化的原理

Struts提供了一個攔截器I18N,該攔截器會自動獲取與國際化相關的請求參數,根據參數值選取相應地資源文件爲程序內部的變量賦值,賦值完成後,頁面按賦值結果顯示。這個請求參數是request_locale,將其追加到URL後面,並按照格式request_locale=language_country賦值。

3.Struts標籤

實現國際化須要使用Struts標籤創建頁面。

4.資源文件命名

國際化的實現依賴於資源文件,一種語言一個資源文件,資源文件的命名決定了其做用範圍,共有三種命名方式:

⑴baseName_language_country.properties

全局範圍資源文件,對整個應用都有效。baseName能夠自定義,放在scr目錄下,在配置文件中配置:

<constant name="struts.sustom.i18n.resources"value="baseName"/>

⑵package_language_country.properties

包範圍資源文件。 

⑶ActionClassName_language_county

Action範文資源文件,放在包內。

  • 優先級由高到底:Action範圍、包範圍、全局範圍。
  • language_country:已經被被標準化,使用時從標準中選取。

5.資源文件的內容

  • key=value
  • key至關於變量名,命名方式:資源文件名+資源中的位置+用途。

6.動態參數

⑴定義

在資源文件中定義動態參數:keyName={0},valueContext,其中佔位符從0開始。

⑵在java代碼中賦值

在java文件中,採用如下形式爲動態參數賦值:

String str=this.getText("key",String[] args);

其中args是一個數組,爲資源文件中的佔位符賦值,這樣就能夠理解爲何不使用*,並且從0開始了。

⑶在JSP頁面賦值

<s:text name="key">
     <s:param><s:property value=""></s:param>//爲第一個佔位符賦值
    <s:param><s:property value=""></s:param>//爲第二個佔位符賦值
</s:text>

<s:param>標籤用做傳遞參數,<s:property>用來取值。

7.引用

  • JSP頁面表單外:<s:text name="key"/>。
  • JSP表單內:全部表單輸入標記顯示文本的屬性統一爲key,key="key(資源文件中key)"。
  • 程序內部:String str=this.getText("key")(該方法只有繼承了ActionSuppor纔有效)。

8.指定資源文件

<s:i18n name="資源文件名相對於src的路徑">
         -------指定資源-------
</s:i18n>

十二 註解式開發

經常使用註釋:

  • @ParentPackage(value=""):指定父包。
  • @Namespace(value=""):指定名稱空間。
  • @Action(name="",results={@Result(name="",type="")}):配置<action>。
  • @Results({@Result(name="",type="",location="")}):配置全局<result>。
  • @InterceptorRef(name=""value=""):配置<interceptor-ref>。
  • @InterceptorRefs({@Interceptor()}):配置所有<interceptorRef>。
相關文章
相關標籤/搜索