一、OGNL表達式語言 Ognl Object Graphic Navigation Language(對象圖導航語言),它是一種功能強大的表達式語言(Expression Language,簡稱為EL),通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現 ...
一、OGNL表達式語言
Ognl Object Graphic Navigation Language(對象圖導航語言),它是一種功能強大的表達式語言(Expression Language,簡稱為EL),通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現欄位類型轉化等功能。
它使用相同的表達式去存取對象的屬性
Struts 2 預設的表達式語言是 OGNL,原因是它相對其它表達式語言具有下麵幾大優勢:
1.支持對象方法調用,如xxx.doSomeSpecial();
2.支持類靜態的方法調用和值訪問,表達式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],
例如:
@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME;
3.支持賦值操作和表達式串聯,如price=100, discount=0.8, calculatePrice(price*discount),這個表達式會返回80;
4.訪問OGNL上下文(OGNL context)和ActionContext;
5.操作集合對象。
6.可以直接new一個對象
OGNL 的應用場景
在標簽中:
<s:property value="user.username" /> //這個 user.username 是個ognl表達式
配置文件中
<result type="redirect" > /main.jsp?name=${userName} </result> //這裡的 ${userName} 是ognl表達式
OGNL
訪問屬性
訪問方法
訪問靜態屬性或方法
訪問構造函數
訪問數組
訪問集合
lambda
二、OGNL 應用舉例
1) 屬性獲取
<s:property value="userName"/> = ${userName} <s:property value="user.userId"/> //對象的某屬性
多級屬性
<s:property value="user.schoolInfo.schoolName" />
2) 調用action中的靜態方法
<s:property value="@cat.action.UserAction@getCountryName()" /> /* 附: 必須要加入一個常量 <constant name="struts.ognl.allowStaticMethodAccess" value="true" /> 才可以在ognl中調用靜態方法 可以在 /org/apache/struts2/default.properties 中,查看到所有常量的值 */
3) 調用JDK 中類的靜態方法
<s:property value="@java.lang.Math@floor(50.99)"/>
4) 調用值棧中對象的普通方法
<s:property value="user.testUser()"/>
5) 調用普通類中的靜態屬性
<s:property value="@cat.bean.UserInfo@country"/>
6) 調用構造方法
<s:property value="new cat.bean.UserInfo(100,'張三').userName"/>
本例要在UserInfo 類中 增加一個構造函數
public UserInfo(int id,String userName){ this.id=id; this.userName=userName; System.out.println("this is a dog"); }
7) 獲取集合中的內容
假如值棧中有一個ArrayList,裡面放的是 叫 userList ,裡面的值是 "aaa","bbb","ccc"則可以: <s:property value="userList[0]"/> 得到 aaa
假如值棧中有一個 List<UserInfo>userList,則可以以數組的方式取數據 <s:property value="userList[0].userName"/>
8) 獲取 map 中的值
<s:property value="userMap['aaa'].userName"/> //aaa 是個key
集合的偽屬性
OGNL 能調用集合的一些特殊屬性,它些屬性並不是javaBean 模式的
主要有以下幾個
size, isEmpty,
iterator
keys,values
next hasNext
nextElement,hasMoreElements
獲取 map 中所有的鍵<s:property value="userMap.keys"/>
取 list 的長度 <s:property value="userList.size"/>
三、ActionContext 和值棧
Strut2中 OGNL Context 實現者為 ActionContext 它的結構如下
--- ValueStack (值棧,是根對象)
--- parameters
--- request
--- session
--- application
--- attr
當 struts 接受一個請求的時候, 會迅速的創建 ActionContext, ValueStack, Action, 然後把 Action放到 ValueStack 中,Action 實例對象可以被 OGNL 訪問。
註意:
在頁面用OGNL設置值()這些訪問的都是上下文中的這些對象的屬性)
<s:property value="#request.requestKey"/> 註意要用# 不用是訪問不到的
<s:property value="#session.sessionKey"/>
用el表達式也能達到相同的效果
${ requestKey}
${ sessionKey}
parameters 的應用場景
<result type="redirect">/b.jsp?name=1234455</result> //註意如果這裡不是redirect 方式,是取不到的 <s:property value=#attr.name / >
四、串連多個Action的值棧
1 寫兩個Action, A,和 B, 裡面分別各自放兩個不同名和欄位,放一個同名的欄位
public class A { private String a_userName; private String a_password; private String xxx; public String execute(){ a_userName="張三"; a_password="sys123"; xxx="這是A里的xxx的值"; return "success"; } } public class B { private String b_goodsName; private String b_goodsNo; private String xxx; public String execute(){ b_goodsName="茶杯"; b_goodsNo="009"; xxx="這是b里xxx的值"; return "success"; }
<package name="packageOne" namespace="" extends="struts-default"> <action name="a" class="cat.action.A"> <result type="chain"> <param name="actionName">b</param> </result> </action> <action name="b" class="cat.action.B"> <result>/Main.jsp</result> </action> </package>
註意:當struts2接收一個請求的時候,它會迅速的創建一個 上下文對象 ActionContex,然後創建 ValueStack,然後創建。
action,再將 action放到ValueStack中
action 已經放到根對象中去了,我們要訪問根對象中的屬性,是不需要#號的
五、使用ognl操作集合
採用ognl表達式創建 List/Map集合對象
<s:set> 標簽用於將某個值放入指定範圍。它有以下兩個屬性
scope:指定變數被放置的範圍,該屬性可以接受application、session、request、 page或action。如果沒有設置該屬性,則預設放置在 OGNL Context 中。
value:賦給變數的值.如果沒有設置該屬性,則將 ValueStack棧項的值賦給變數。
//例一:操作 List集合 <s:set var="userList" value="{'黃世仁','鄧世昌','李剛','楊達才'}" /> ---//例一 s:iterator 標簽在迭代時有一個特點,會把當前迭代的對象放在值棧的棧頂 <s:iterator value="#userList"> <s:property /> <br> //對於 s:property 如果不指定value 則value取值棧頂的值 </s:iterator> ---//例二 <s:iterator value="#userList" id="user"> <s:property value="user" /> <br> //這裡指定了 property的 value 就是 上邊的 id ${user } //這裡用El表達式也行 </s:iterator> --- //例三 (處理聲明時使用了scope 屬性的集合) <s:set var="userList" value="{'黃世仁','鄧世昌','李剛','楊達才'}" scope="session" /> <s:iterator value="#session.userList" id="user" > <s:property value="user" /> <br> ${user } </s:iterator>
//例二:操作 Map 集合 //註意,如果map中key對應的value是數字型,則可以不用''號 <s:set var="userMap" value="#{'1號首長':'江','二號首長':'濤','三號首長':'平' }" /> <s:iterator value="#userMap" > <s:property value="key"/>= <s:property value="value"/> <br> </s:iterator> 對於集合類型,OGNL表達式可以使用in和not in兩個元素符號判斷元素是否在某個集合中============= <s:if test="'aaa' in {'aaa','bbb'}"> 在 </s:if> <s:else> 不在 </s:else> 如果集合是個變數也可以 <s:if test="'黃世仁' in #userList"> 在 </s:if> <s:else> 不在 </s:else>
六、ognl表達式的投影功能
1 對項目添加資料庫訪問,然後在Action中,查出用戶列表 userList 放在值棧里
2 UserManage.jsp 中
==全部取出
<s:iterator value="userList"> <s:property value="userId"/>=<s:property value="userName"/><br> </s:iterator>
==帶投影的
?:獲得所有符合邏輯的元素。
^:獲得符合邏輯的第一個元素。
$:獲得符合邏輯的最後一個元素。
<s:iterator value="userList.{?#this.id>200}"> <s:property value="id"/>=<s:property value="userName"/><br> </s:iterator>
==用jstl的
<c:forEach var="user" items="${userList}" >
${user.userId} | ${user.userName} <br>
</c:forEach>
七、struts 標簽庫概述
Struts2標簽分類
(1)UI標簽:(User Interface, 用戶界面)標簽,主要用於生成HTML元素標簽,
UI標簽又可分為表單標簽非表單標簽
(2)非UI標簽,主要用於數據訪問,邏輯控制等的標簽。非UI標簽可分為流程式控制制標簽
(包括用於實現分支、迴圈等流程式控制制的標簽)和數據訪問標簽(主要包括用戶輸出ValueStack中的值,完成國際化等功能的)
(3)ajax標簽
==流程式控制制標簽
if
elseif
else
append //合併多個集合到一個新集合里
generator //把字元串按某種標誌進行切割成集合
iterator
merge //合併多個集合到一個新集合里
sort
subset
==數據訪問標簽
a
action
bean
date
debug
i18n
include
param
property
push
set
text
url
八、Struts2 數據訪問標簽
(一)<s:set> 標簽 //設置值
<s:set var="dog" value="'安倍晉三'" /> 嚴重註意,dog 是要用單引號擴起來的, var 可以換寫成name,但name被廢棄了 //取值: <s:property value="#dog"/> //可以 <s:property value="dog"/> //可以 ${dog } //可以 <s:debug />
結過觀察,發現如果不指定scope, 它是放在 Stack Context (其實就是context map, Acton Context,OGNL Context,) 中的<hr/>
<c:set var="num1" value="ooooooooo" scope="request" /> 這是jstl 標簽
關於 scope :
The scopes available are as follows :-
application - the value will be set in application scope according to servlet spec. using the name as its key
session - the value will be set in session scope according to servlet spec. using the name as key
request - the value will be set in request scope according to servlet spec. using the name as key
page - the value will be set in page scope according to servlet sepc. using the name as key
action - the value will be set in the request scope and Struts' action context using the name as key '
關於 set 的 參數
Name Required Default Evaluated Type Description
id false false String Deprecated. Use 'var' instead //Deprecated 這個單詞是不選成,反對的意思
name false false String Deprecated. Use 'var' instead
scope false action false String The scope in which to assign the variable. Can be application, session, request, page, or action.
value false false String The value that is assigned to the variable named name
var false false String Name used to reference the value pushed into the Value Stack
2.<s:property> 標簽
用於輸出表達式的值,如果指定value 屬性,就輸出value 對應的值,如果沒有指定,它會輸出ValueStack 棧頂的值
1,訪問Action值棧中的普通屬性:
<s:property value="attrName"/>
2,訪問Action值棧中的對象屬性(要有get set方法):
<s:property value="obj.attrName"/> <s:property value="obj1.obj2.attrName"/>
3,訪問值棧中對象屬性的方法
<s:property value="obj.methodName()"/>
4,訪問值棧中action的普通方法:
<s:property value="methodName()"/>
5,訪問靜態方法:
<s:property value="@com.softeem.LoginAction@methodName()"/>
6,訪問靜態屬性:
配置屬性文件,允許ognl訪問靜態方法struts.ognl.allow...=true
<s:property value="@com.softeem.LoginAction@attrName"/>
7,訪問Math類的靜態方法:
<s:property value="@@min(9,7)"/>
8,訪問普通類的構造方法:
<s:property value="new com.softeem.User(2)"/>
9,訪問集合:
①list集合對象
<s:property value="listName"/>
②list集合中的某個元素
<s:property value="listName[1]"/>
③list中某個屬性的集合
<s:property value="listName.{field}"/>
④list中某個屬性集合的特定值
<s:property value="listName.{field}[0]"/>
⑤訪問set
<s:property value="setName"/>
⑥訪問set中某個元素
<s:property value="setName[0]"/>
⑦訪問map
<s:property value="mapName"/>
⑧根據key訪問Map中的元素
<s:property value="mapName.username"/> <s:property value="mapName['username']"/> <s:property value="mapName[/"username/"]"/>
⑨訪問map中所有的key
<s:property value="mapName.keys"/>
10,訪問map中所有的values
<s:property value="mapName.values"/>
11,訪問map的大小
<s:property value="mapName.size()"/>
12,投影
<s:property value="listName.{?#this.age==1}"/>
<s:property value="listName.{^#this.age>1}"/>
<s:property value="listName.{$#this.age==1}"/>
<s:property value="listName.{$#this.age==1}.{age}==null"/>
[]:<s:property value="[0]"/>值棧中的對象
default:可選屬性,如果需要輸出的屬性值為null,則顯示該屬性指定的值
escape:可選屬性,指定是否格式化HTML代碼,不搭薦,應該用escapeHtml
value:可選屬性,指定需要輸出的屬性值,如果沒有指定該屬性,則預設輸出ValueStack棧頂的值。
id:可選屬性,指定該元素的標識 這個屬性好象在新版本中已經沒了
Name Required Default Evaluated Type Description
default false false String The default value to be used if value attribute is null
// escape false true false Boolean Deprecated. Use 'escapeHtml'. Whether to escape HTML
escapeCsv false false false Boolean Whether to escape CSV (useful to escape a value for a column)
escapeHtml false true false Boolean Whether to escape HTML
escapeJavaScript false false false Boolean Whether to escape Javascript
escapeXml false false false Boolean Whether to escape XML
value false <top of stack> false Object Value to be displayed
//例子: <s:set var="userName" value="'struts標簽真爛'" /> 嚴重註意, 裡面要有單引號 <s:property value="#userName" /> //正常該加 # <s:property value="userName" /> //不加也能取出來 <c:out value="${userName}"></c:out> //用jstl標簽也能取
上邊的功能完全可以用 jstl標簽取代 如下:
<c:set var="userMsg" value="struts 標簽庫的設計者是白痴" /> ${userMsg} <c:out value="${cat_name}" default ="--------------------"/>
(二)<s:action> 標簽
是用來調用Action的標簽,在JSP中頁面中,可以具體指定某一命名空間中的某一Action。而標簽的主體用於顯示及渲染Actionr的處理結果。
action標簽有如下幾個屬性:
1、id: 可選,作為該action的引用ID //作廢
2、name: 必填,調用action的名字
3、namespace: 可選,action所在的nqmespace
4、executeResult,可選,指定是否將action的處理結果頁麵包含到本頁面。
5、ignoreContextParame: 可選,指定該頁面的請求參數是否需要傳入action.
6、var
7、rethrowException
8、flush
//這個userAction的名字,是給本頁面中別的標簽引用用的, <s:action name="listUser" var="page_userAction" executeResult="false"> 如果把executeResult換成true則會把result對應的視圖傳過來 <s:param name="paramAAA">調Action時傳一個參數</s:param> 可以通過這種方法給被調用的action傳參數,在action中可以用值棧的方式取到它 </s:action> <s:iterator value="#userAction.userList"> 可以這樣取出被調用的action中的值,註意,這些值是放在 StackContext中的,所以要加# <s:property value="userName" /> </s:iterator> <c:forEach var="user" items="${page_userAction.userList}"> 使用jstl 標簽,把傳過來的值生成一組覆選框 ${user.userName}: <input type="checkbox" value='${user.userId}' /> </c:forEach> <s:debug />
== <s:push> 把表達式的值放入valueStack 的棧頂
== <s:url> 標簽
<s:url action="UserAction" namespace="/test">
<s:param name="userId" value="10"/>
<s:param name="userName" value="'趙鐵冷'"/> 需要註意: 如果value是漢字,早要加單引號 ,如果是中文,會被url編碼
</s:url>
//實例: 訪問UserAction <a href='<s:url action="UserAction" namespace=""> //UserAction是配置在xml文件中的名字 <s:param name="userId" value="10"/> <s:param name="userName" value="'趙鐵冷'"/> </s:url>' > 去Action里串串門 </a>
知識點回顧: 在UserAction中取出 userName
可以在UserAction 中定義一個 userName 欄位,生成get和set方法,這是前面常用的 struts取請求參數的方法。硬生生的取得request對象,從requet中取得,這是以前常用的方法,如下:
HttpServletRequest request=ServletActionContext.getRequest(); String tempStr=request.getParameter("userName"); tempStr=new String(userName.getBytes("ISO8859-1"),"UTF-8"); String userName=URLDecoder.decode(tempStr,"UTF-8"); //其實在這個地方不decode也行 //列印 userName <s:set name="myurl" value="'http://www.baidu.com'"/> <s:url value="#myurl" /> <s:url value="%{#myurl}" /> //它的value預設接收的是字元串,而不是ognl表達式,如果想用表達式得轉一下
九、Struts2 流程式控制制標簽
==iterator標簽
它的參數,還有 begin 和 end 從第幾個元素開始,迭代幾個 begin ="2" end="4",用於對集合進行迭代,這裡的集合包含List、Set和數組。
<s:set name="list" value="{'西瓜','哈密瓜','大傻瓜','半拉瓜'}" /> <s:iterator value="#list" var="瓜名" status="st"> //這裡的var 也可以用 id,但id 不推薦 <font color= <s:if test="#st.odd">red</s:if> <s:else>blue</s:else> > <s:property value="瓜名"/> count: <s:property value="#st.getCount()" /> index: <s:property value="#st.getIndex()" /> </font><br> </s:iterator>
== if else
<s:set var="廣告名" value="'腦白金'" /> <s:if test="#廣告名=='腦白金'"> //註意,這裡不用 #可就不行了 腦殘廣告,傻子才買 </s:if> <s:elseif test="#廣告名=='夢酒'"> 五糧醇風,古方秘制,欽譽中外,獨占鰲頭 </s:elseif> <s:else> 說不定是就鍋王胡師傅 </s:else>
可以用jstl代替上邊的功能:
<c:choose> <c:when test="${廣告名=='夢酒'}"> 一看就想喝的夢酒</c:when> <c:when test="${廣告名=='腦白金'}">讓人想吐的腦白金</c:when> <c:otherwise>估計十有八九是鍋王胡師傅</c:otherwise> </c:choose>
//對於上例,可以指定 scope <s:set name="廣告名" value="'腦白金'" scope="request"/> 指定泛圍為 request <s:if test="#request.廣告名=='腦白金'"> 取的時候也要用 request. 腦殘廣告,傻子才買 </s:if> <s:elseif test="#request.廣告名=='夢酒'"> 五糧醇風,古方秘制,欽譽中外,獨占鰲頭 </s:elseif> <s:else> 說不定是就鍋王胡師傅 </s:else>
十、Struts2 UI標簽
== 主題和模板
Struts2提供了三種主題,ajax, simple, css_xhtml,xhtml,它預設的是xhtml主題,開發時我們一般都選simple。因為Struts2所有的UI標簽都是基於主題和模板的,主題和模板是Struts2所有UI標簽的核心。模板是一個UI標簽的外在表示形式,例如:當我們使用<s:select ... ... />標簽時,Struts2就會根據對應select模板來生成一個有模板特色的下拉列表框。如果為所有的UI標簽都提供了對應的模板,那麼這系列的模板就形成了一個主題。
對於一個JSP頁面里包含的UI標簽而言,即可以直接設置該UI標簽需要使用的模板,也可以設置該UI標簽使用的主題。實際上對開發者而言,並不推薦直接設置模板屬性,而是應該選擇特定主題。設置主題的方法有以下幾種:
1,通過設定特定UI標簽上的theme屬性來指定主題。
2,通過設定特定UI標簽外圍的Form標簽的theme屬性來指定主題。
3,通過取得page\request\session\application 會話範圍內以theme為名稱的屬性來確定主題。
4,通過取得名為struts.ui.theme的常量(預設值是xhtml)來確定主題,該常量可以在struts.properties文件或者struts.xml文件中確定。如:
<constantname="struts.ui.theme" value="simple" />
==表單標簽
1 <s:checkboxlist>
====如果集合為list
<s:checkboxlist name="list" list="{'Java','.Net','RoR','PHP'}" value="{'Java','.Net'}"/>
生成如下html代碼:
<input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label> <input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label> <input type="checkbox" name="list" value="RoR"/><label>RoR</label> <input type="checkbox" name="list" value="PHP"/><label>PHP</label>
它預設會生成一些主題相關的tr td等,如果不想要,可以在struts.xml中配置 <constant name="struts.ui.theme" value="simple" />
====如果集合為MAP
<s:checkboxlist name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value" value="{1,2,3}"/>
生成如下html代碼:
<input type="checkbox" name="map" value="1" checked="checked"/><label>瑜珈用品</label> <input type="checkbox" name="map" value="2" checked="checked"/><label>戶外用品</label> <input type="checkbox" name="map" value="3" checked="checked"/><label>球類</label> <input type="checkbox" name="map" value="4"/><label>自行車</label>
====如果集合是里放的是javaBean
<% Person person1 = new Person(1,"第一個"); Person person2 = new Person(2,"第二個"); List<Person> list = new ArrayList<Person>(); list.add(person1); list.add(person2); request.setAttribute("persons",list); %> <s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
Personid和name為Person的屬性
生成如下html代碼:
<input type="checkbox" name=“beans" value="1"/><label>第一個</label> <input type="checkbox" name=“beans" value="2"/><label>第二個</label>
例子:在 UserManage.jsp 中 (因為前面已經做過列出用戶的例子,所以現在可以直接取內容)
<s:checkboxlist name="beans" list="userList" listKey="id" listValue="userName"/>然後 訪問 http://localhost:8080/OGNL/UserAction!getAllUser
2 <s:radio>
該標簽的使用和checkboxlist覆選框相同。如果集合里存放的是javabean(personid和name為Person的屬性)
<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
生成如下html代碼:
<input type="radio" name="beans" id="beans1" value="1"/><label>第一個</label> <input type="radio" name="beans" id="beans2" value="2"/><label>第二個</label>
如果集合為MAP
<s:radio name="map" list="#{1:'瑜珈用品',2:'戶外用品',3:'球類',4:'自行車'}" listKey="key" listValue="value“ value="1"/>
生成如下html代碼:
<input type="radio" name="map" id="map1" value="1"/><label for="map1">瑜珈用品</label> <input type="radio" name="map" id="map2" value="2"/><label for="map2">戶外用品</label> <input type="radio" name="map" id="map3" value="3"/><label for="map3">球類</label> <input type="radio" name="map" id="map4" value="4"/><label for="map4">自行車</label>
如果集合為list
<s:radio name="list" list="{'Java','.Net'}" value="'Java'"/>
生成如下html代碼:
<input type="radio" name="list" checked="checked" value="Java"/><label>Java</label> <input type="radio" name="list" value=".Net"/><label>.Net</label>
十一、<s:token > 用來防止表單重覆提交
例子:重覆提交的例子
1.
private UserInfo userInfo; public String add(){ UserDao dao=new UserDao(); dao.addUser(this.userInfo); return "success"; }
2 struts.xml中 配置Action
<action name="UserAction_Add" class="cat.action.UserAction" method="add">
<result name="success">/Succ.jsp</result>
</action>
3 UserAdd.jsp中
<s:form action="UserAction_Add" namespace="" method="post"> 姓名:<s:textfield name="userInfo.userName" /> 賬號:<s:textfield name="userInfo.userId" /> 密碼:<s:password name="userInfo.password" /> 備註:<s:textarea name="userInfo.note" /> <s:submit value="提交" /> </s:form>
4 進行提交,看刷新後的重覆提交的結果
使用 tokey 標簽防止重覆提交,用法如下:
1 在表單中加入<s:token />
...
<s:token/> //加入這個tokey
....
2 在XML中
<action name="UserAction_Add" class="pinkcat.action.UserAction" method="add"> <interceptor-ref name="defaultStack"/> //註意,預設的攔截器棧一定要加上 <interceptor-ref name="token"/> <result name="invalid.token">/UserAdd.jsp</result> //設置用戶如果重覆提交,則返回添加頁 <result name="success">/Succ.jsp</result>
3 在UserAction 中 讓其繼承自 extends ActionSupport
十一、攔截器
Interceptor
和過濾器很象,但它只捕獲 Action。在Struts2 中 許多Action通常都有一些共同需要關心的問題,比如有一些Action 它們都需要對頁面上的輸入進行校驗,有一些Action需要對上傳的文件進行一下預處理,還有一些Action可能需要防止表單的重覆提交 (double submit) 還有很多Action需要在頁面顯示之前將下拉列表和其它一些控制項事先裝好值,通過攔截器策略,Struts2 框架使得共用這些問題更容易。
攔截器能在 Action 被調用之前和被調用之後執行一些 "代碼",Struts2 框架的大部分功能都是通過攔截器來實現的,如防止重覆提交,類型轉換,對象封裝,校驗,文件上傳,頁面裝載等等。每一個攔截器都是獨立裝載的(pluggable) ,可以根據實際的需要為每一個Action 配置需要用的攔截器. 例如: 一個Action 需要用到類型轉換器 和 文件上傳,那麼我們可以給它設置兩個攔截器。
攔截器是Struts2 框架的核心,包括解析請求參數,將請求參數賦值給Action屬性,執行數據校驗,文件上傳等工作都是用它實現的Struts2設計的靈巧性,更多的得益與攔截器的設計.當需要進行擴展時,只需要提供對應的攔截器,並將它配置在Struts2容器中即可,反之可以摘掉 (AOP) 面向切麵。
十二、struts2 自帶的攔截器
Struts2 內建了大量攔截器,它們 以 name-class 對的形式配置在 struts-defalut.xml 文件中
alias: 實現在不同請示中相似參數別外的轉換
autowiring 自動裝配的攔截器,用於和Spring 進行整合
chain 構建一個Action鏈,使當前的Action可以訪問前一個Action的屬性 一般和 <result type="cahin"> 一起使用
conversionError 處理類型轉換錯誤 它負責將類型轉換錯誤從ActionContext 中取出,並轉換成Action 的FieldError 錯誤
createSession 負責創建一個HttpSession對象
debugging 當使用Struts2 開發模式時,它會提交更多的調試信息
execAndWait 後臺執行Action,負責將等待畫面發送給用戶
execption 處理異常,將異常映射為結果
fileupload 文件上傳,解析表單域中的內容
i18n 國際化,負責把所選語言,區域放入用戶Session中
ogger 日誌記錄,主要是輸出Action 的名字
model-driven 一個用於模型驅動的攔截器, 當某個Action 類實現了 ModeDriven介面時,它負責把getModel() 方法的結果堆入ValueStack中
scope-model-driven
params 最基本的一個攔截器,負責解析Http請示中的參數,並將參數值設置成Action對應的屬性值
prepare 如果Action實現了Prepareable 介面,會調用訪欄截器中的prepare()方法
static - params 將XML中<action>標簽下的<param>標簽中的參數傳入 Action
scope 範圍轉換 可以將Action 狀態信息保存到 HttpSession 或 ServletContext 範圍內
servlet-config 如果某個Action 需要直接訪問 Servlet Api ,則通過它實現(儘量避免在Action中直接訪問Servlet Api,高耦合)
roles JAAS java許可權和認證服務 攔截器,有權才能調用該攔截器攔截的Action
timer 輸出Action執行時間 分析Action性能的瓶頸時有用
token 阻止重覆提交
token -session 和上面相似,只是把token保存在HttpSesison中
validation 通過執行在xxxAction-validation.xml中定義的校驗器.從而完成數據校驗
workflow 負再調用 Action類中的 validation 方法,如失敗則返回 Input視圖
十三、自定義攔截器
自定義攔截器有三種方式
1) 實現 Interceptor 介面
2) 繼承自 AbstractInterceptor
3) 繼承自 MethodFilterInterceptor
例一 計算一個Action的執行時間的攔截器,繼承自 AbstractInterceptor方式實現
1) 定義一個攔截器類
public class MyInterceptor extends AbstractInterceptor { //這個String 代表執行完Action後的返回值 public String intercept(ActionInvocation invocation) throws Exception { System.out.println("攔截開始"); long begintime=System.currentTimeMillis(); String result=invocation.invoke(); //執行A ction中的對應的方法 long endtime=System.currentTimeMillis(); System.out.println("該action 共執行了:"+(endtime-begintime)+" ms"); System.out.println("攔截結束"); return result; } }
關於 ActionInvocation
ActionInvocation就是Action的調用者。ActionInvocation在Action的執行過程中,負責Interceptor、Action和Result等一系列元素的調度。Interceptor通過ActionInvocation可以完全的改變 Action行為:不讓它執行、改變返回值、甚至可以細顆粒的操作Action的方法。它有getActionProxy(),getResultCode();getInvocationContext();getAction() 等方法。比如 可以通過 getAction 拿到Action對象,並執行裡面的方法:
MoreMethodAction M=(MoreMethodAction)invocation.getAction();
M.test();
或者通過它拿到 ActionContext 對象
ActionContext act=invocation.getInvocationContext();
2) 在配置文件中配置
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="defaultStack" /> //如果引入了自定義的攔截器,系統就不再提供預設的攔截器棧了,所以要自己手功引入 </action> </package>
3) action
public class UesrAction { public String add(){ System.out.println("add 方法被調用了"); return "success"; } public String del(){ System.out.println("del 方法被調用了"); return "success"; } public String update(){ System.out.println("update 方法被調用了"); return "success"; } public String search(){ System.out.println("search 方法被調用了"); return "success"; } }
例二 靈活的限定攔截哪些方法,不攔截哪些方法= //通過繼承 MethodFilterInterceptor 方式實現
1) 攔截器
public class MyInterceptor2 extends MethodFilterInterceptor{ protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("MethodFilterInterceptor 啟動"); String result=invocation.invoke(); System.out.println("MethodFilterInterceptor 結束"); return result; } }
2) 配置文件
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> <interceptor name="myInte_BBB" class="cat.interceptor.MyInterceptor2" > //這是針對 MyInterceptor2 的 <param name="excludeMethods">search,update,del</param> //這裡的面的方法,不會被攔截 </interceptor> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="myInte_BBB" /> //引用聲明的攔截器 <interceptor-ref name="defaultStack" /> </action> </package>
3) action 同上例一樣,略
訪問 userAction_add,userAction_del 等方法,可以發現, <param name="excludeMethods">search,update,del</param> 中聲明的這幾個方法沒有被攔截。
附: 使用攔截器棧
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> <interceptor name="myInte_BBB" class="cat.interceptor.MyInterceptor2" > <param name="excludeMethods">search,update,del</param> </interceptor> <interceptor-stack name="myStack"> //將自己聲明的兩個攔截器和系統的 defaultStack 組成一個攔截器棧 <interceptor-ref name="myInte_BBB" /> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myStack" /> //引用攔截器棧 </action> </package>
例三 系統攔截器的調用,登錄延時提醒ExecuteAndWaitInterceptor 很適合在後臺長時間運行的action時,它可以可為用戶一個友好的等待界面,例如進度條。
不在預設的 defaultStack 中
1)
<action name="loginAction" class="cat.action.UesrAction" method="login">
<result name="success">/main.jsp</result>
<result name="wait" >/wait.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="execAndWait">
<param name="delay">2000</param>
</interceptor-ref>
</action>
2) wait.jsp 中
<meta http-equiv="refresh" content="1;URL=loginAction" > //或 response.setHeader("refresh", "3;URL=loginAction");
3) 在loginAction 設置 Thread.sleep(4000)
本例說明:
當調用 loginAction 的時候,程式會根據 配置文件中 delay 屬性( 2000 )等待Action先執行 2000 毫秒, 但由於Action 中 sleep(4000), 所以它沒執行完,這時攔截器就把請求發到 wait 視圖上去了,wait 視圖中每隔一秒 訪問一下 loginAction,但這時loginAction對應的那個 Action還在沉睡中,所以訪問也是無效的,(這些請求都被忽略了?) 直到它睡醒, 這時刷過去的請求就直接到result上了。
例四 Session 許可權攔截器
public class SessionInterceptor extends MethodFilterInterceptor{ @Override protected String doIntercept(ActionInvocation invocation) throws Exception { //方式一 (取到sesssion中的內容) /*ActionContext ctx=ActionContext.getContext(); ctx.getSession()*/ //方式二 //ServletActionContext.getRequest().getSession(); //方式三 UserInfo user=(UserInfo)invocation.getInvocationContext().getSession().get("userInfo"); if(user!=null){ return invocation.invoke(); } else{ System.out.println("沒有登錄"); ActionContext.getContext().put("msg","您還沒有登錄"); return "no_login"; } } } <interceptor name="session_Inte" class="cat.interceptor.SessionInterceptor" > <param name="excludeMethods">login</param> </interceptor> ... <action name="sessionAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="session_Inte" /> <interceptor-ref name="defaultStack" /> </action>
十四、在struts2中使用Ajax
(通過 stream 類型的 result 實現)
public class AjaxAction { private String userName; private String result; ... get set 方法 public String checkUser(){ if("admin".equals(userName)){ result="用戶名已經被別人使用"; return "success"; } else{ result="用戶名可用"; return "success"; } } //把字元串轉成 ByteArrayInputStream public InputStream getInputStream() throws UnsupportedEncodingException { return new ByteArrayInputStream(result.getBytes("utf-8")); }
頁面: ajax_test.jsp
$(function() { $("#btn1").click(function(){ $.ajax({ url:"ajaxAction_checkUser", data:{userName:"admin"}, type:"post", success:function(data){ $("#divResult").html(data); } }); }); }); //或 $("#div2").load("ajaxAction_checkUser",{userName:"admin"}); //這樣寫更簡單 <