接著Struts2_3_day的講
注:使用Struts2的< s:debug>< /s:debug>就可獲取數據儲存的分布圖
StrutsPrepareAndExecuteFilter都會創建一個ActionContext和ValueStack對象, 所以Struts2的數據存儲分為兩類: ActionMap(contextMap)以及ValueStack; ActionMap中都是以map的形式存取數據, 而ValueStack是以Stack的方式存取數據(底層就是List)
ActionMap數據操作
利用ActionMap存儲數據
ActionMap是一個大的map, 而這個大map中又存有application, session, request, parameters, attr的小map, 所以在ActionMap中存放數據的時候既能存入大map中, 也能存入小map中, 在小map中還能存入map, 形成多層的存儲, 如下代碼操作:
1.直接在ActionMap中存入數據, 使用ActionMap的靜態方法getContext獲取ActionMap對象, 然后直接putActionMap context = ActionMap.getContext();context.put("contextMap", "contextMap");2.往ActionMap中的session對象中存入數據使用ActionMap中的session, session也是一個mapMap<String, Object> sessionAttribute = context.getSession();sessionAttribute.put("sessionMap1", "sessionMap1");使用原始HttpSession對象存儲數據HttpSession session = ServletActionContext.getRequest().getSession();session.setAttribute("sessionMap2", "sessionMap2");3.往ActionMap的application中存入數據使用使用ActionMap中的ServletContext域Map<String, Object> applicationAttribute = context.getApplication();application.put("applicationMap1", "applicationMap1");使用原始ServletContext對象ServletContext application = ServletActionContext.getServletContext();application.setAttribute("applicationMap2", "applicationMap2");
注: 無論使用ServletActionContext中的存儲方式, 還是使用ActionMap.getContext().getXXX()的方式, 他們所指向的存儲區域都是一樣的, 例如上面的applicationMap1和applicationMap2, 他們二者的數據都是存放在同一片儲存區域的, 同理sessionMap1與sessionMap2也是一樣的
獲取ActionMap中數據
可以直接使用Map的get, 或者使用域對象的getAttribute方法來獲取ActionMap中數據, 但是大多數使用OGNL表達式在jsp中獲取ActionMap數據, 例如:
// 使用OGNL表達式獲取域對象數據, OGNL表達式必須寫在Struts2的標簽中
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="#session.sessionMap1">
//在使用OGNL表達式取用ActionMap的數據的時候, 使用"#" //借用上面代碼的存儲方式, 因為sessionMap1,以及sessionMap2是存入session中, 所以直接使用#session.sessionMap1
//數據的查找順序是: 先去ActionMap中查找session區域, 找到之后在session區域中查找是否具有key為sessionMap1的數據//同理, 當查找applicationMap1也是直接使用:<s:property value="#application.applicationMap1">
ValueStack數據操作
利用ValueStack存儲數據
正如前面所說的, ValueStack就是一個Stack結構, 當我們向ValueStack中存入數據的時候就是進行入棧, 出棧的操作
下面先介紹獲取ValueStack對象引用的3種常見方式:
1.使用<s:debug></s:debug>之后發現, 在request中存有ValueStack的引用, 所以可以使用get的方式獲取:ActionContext context = ActionContext.getContext();Map<String, Object> request = (Map<String, Object>) context.get("request");ValueStack vs = (ValueStack)request.get("struts.valueStack");2.使用ServletActionContext獲取request對象, 然后再通過getAttribute獲取:ServletRequest request = ServletActionContext.getRequest();ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");3.直接使用ActionContext對象的getValueStack方法獲取:ValueStack vs = ActionContext.getContext().getValueStack();注: 通過上面的3種方式獲取的vs都是同一個對象
當我們獲取到ValueStack對象之后就可以對ValueStack對象做push, peek/pop操作以此達到存儲數據的目的, 例如:
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(new Student("A", 18)); //對一個Student對象做入棧操作
注: 默認動作類對象會自動存入ValueStack中, 因此可以通過OGNL表達式獲取默認動作對象的屬性方法(對該屬性一定要寫set/get方法)
獲取ValueStack中數據
當使用動作類進行ValueStack存儲數據的時候, 一定要記住: 如果直接想通過OGNL表達式獲取, 此時數據只具有request的作用域范圍, 當使用頁面重定向后, 就不能訪問到剛才動作方法中存入ValueStack中的數據
1.可以使用pop/peek直接獲取ValueStack數據或者jsp中使用標簽的方式獲取ValueStack數據; 2.jsp中操作: 使用< s:property value="name" />這里的name是一個OGNL表達式, 用于獲取Student中的name屬性;3.與獲取ActionContext數據不同的是, value后面的參數少了一個"#", **"#"是作為獲取ValueStack與ActionMap中數據的一種區別**;4.在ValueStack中, 查找方式是: 在Stack中依次遍歷每個對象, 查看當前對象的屬性是否是value后面的參數值,如果是就代表找到, 就不再往后查找. 例如: 當動作類中也具有一個name屬性, 但是現在棧頂是一個Student對象, 當找到Student對象中的name屬性的時候就不再往后找, 即使動作類中也具有name;5.在OGNL表達式中可以多級訪問: 比如ValueStack中存入的數據是一個Map對象map, Map對象中存有一個Student對象s1, 當我們想要獲取此時Student對象的name屬性的時候, 就可以寫:< s:property value="s1.name" />
ValueStack的set與setValue方法的區別
set方法:
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(new Student("A", 19));
vs.set("s1", new Student("B", 20));如上述代碼, 當vs使用set操作的時候, 操作的元素是ValueStack中的元素.
如果棧頂是一個Map, 就直接將一個key為s1, value為Student對象存入Map中
如果棧頂不是一個Map, 則ValueStack新建Map<String, Object>對象, 然后將key為s1, value為Student對象存入Map中當連續多次vs.set()的時候, 所有的set的對象都是存入一個Map對象中, 例如:vs.set("s1", new Student("A", 20));vs.set("s2", new Student("B", 10));vs.set("s3", new Student("C", 24));當使用OGNL表達式訪問棧中map中的元素, 采用: <s:property value="s1.name"/>, 而不是<s:property value="#s1.name">, 這里是在ValueStack中取元素, 而不是ActionMap
在獲取元素的時候, 元素對象一定要寫set/get方法
這里別理解為連續set是建立多個map, 只有在下面這種情況下, 才是建立多個Mapvs.set("s1", new Student("A", 20)); //新建一個mapvs.push(new Student("C", 24));vs.set("s2", new Student("B", 10)); //又新建一個mapsetValue方法:
setValue對ValueStack中存儲元素值的修改操作:vs.setValue("name", "X");將棧中第一個name元素修改為X, 借用上面代碼, 此處的修改是將"C"變為"X"setValue的第一個參數是一個OGNL表達式setValue對ActionMap中存儲元素值的修改操作:vs.setValue("#name", "X");這里修改的name屬性, 是對ActionMap中key為name的value進行修改
ValueStack的findValue
在JSP上使用OGNL表達式獲取ActionMap或者ValueStack中的內容的時候, 都是執行如下操作
vs.findValue("name"); //在ValueStack中查找名為name屬性的值
vs.findValue("#name"); //在ActionMap中查找key為name的value值
Struts中的EL
JSP中的EL表達式獲取的是請求域中對象(page -> request -> session -> application)
Struts2的StrutsRequestWrapper對request重新做了封裝, 對getAttribute進行改寫, 當獲取完request域中對象后, 會先去ValueStack, 然后是ActionMap中尋找需要的對象
Struts2對EL表達式進行了改寫使得獲取對象變為: page -> request -> ValueStack -> ActionMap -> session -> application
注: 當使用OGNL表達式查找屬性值的時候, 如果寫做< s:property value=“name” />, 它會先去ValueStack中查找, 當沒找到的時候再去ActionMap中, name作為key找一遍.
Struts2中#, $, %的使用
OGNL表達中獲取ActionMap中的Key為key的value值:<s:property value="#name"> //獲取Key為name的value值創建Map對象的時候, 主要用于CheckBox, radio的多條件選擇:<s:radio list="#{ 'male':'男', 'female':'女' }">
JSP中使用EL表達式:${name}
在xml中配置文件名, 或者使用靜態函數(避免在JSP中寫<%%>代碼)${@java.lang.Math.random()}
強制將一個字符串看做是OGNL:<s:property value="%{name}">
Struts2中常用標簽(使用OGNL配合使用)
- iterator標簽
主要用于迭代遍歷元素, 類似于JSP中的foreach標簽, 下面舉例說明:
Action類:
//使用list模擬從數據庫提取Student數據, 顯示在結果視圖中public class DemoAction extends ActionSupport{private List<Student> students;public String findAll(){ //action方法students = new ArrayList<Student>(3);students.add(new Student("A", 20));students.add(new Student("B", 30));students.add(new Student("C", 10));return SUCCESS;}public List<Student> getStudents(){return students; }public void setStudents(List<Student> students){this.students = students;}}
//JSP中iterator操作<s:iterator value="students" var="s" status="vs"><tr><td><s:property value="#vs.index"/><br/></td><td><s:property value="#s.name"/><br/></td><td><s:property value="#s.age"/><br/></td> </tr></s:iterator>
注:
1.在遍歷的過程中具有兩種操作用于暫時存儲需要遍歷的變量(方便獲取該變量的內部屬性):a.使用ActionMap: 當指定在iterator中指定var屬性的時候,Struts2會將var作為key, 當前變量作為value存入ActionMap中, 便于后面通過OGNL表達式獲取該變量的內部屬性, 因為key是固定的, 所以下一個元素存入ActionMap中會將上一個元素的value覆蓋, 這樣做也是為了減少空間占用,遍歷結束后會將最后存在ActionMap中的變量removeb.使用ValueStack: 如果沒有指定var, 那么Struts2會直接將該變量壓入棧中, 當使用完該變量進行pop操作, 原因同上2.status主要是用來記錄遍歷的數據屬性(以status作為key, 數據屬性作為value存入ActionMap中):status具有index, count, odd, even, first, last屬性, 表示下標, 遍歷的數據和, 奇數位, 事件, 第一個值, 最后一個值, 具體用法自行百度3.在上面例子中, 可以使用value="name"的OGNL表達式來輸出變量屬性, 也可以使用EL表達式${name}來輸出變量屬性
- set標簽
與JSP中的set標簽一樣, 就是聲明一個變量, 然后對該變量賦值, 設置訪問范圍
<s:set value="a" var="1111" scope="session"/>
<s:property value="#1111"/>
聲明一個變量"1111", 賦值為"a"的字符串, 設定訪問范圍為sessionset設置的變量是存入Map中的, value代表屬性值, var代表Key值, scope取值范圍application, session, request, page, action, 當scope不寫的時候默認是action, ActionMap與request中各存一份
-
action標簽
在JSP中調用action方法: < s:action name=“action1” executeResult=“true”/> ,executeResult表示是否顯示執行動作結果, 默認值false
-
url和a標簽
url標簽主要用于獲取url地址
a標簽用于向一個url地址進行跳轉, 就< a href="…"/ >
例:<s:url action="action1" value="action1" var="url"><s:param name="username" value=" 'test' "></s:param></s:url>action: 獲取值為action1的動作請求地址, 類似于EL表達式: ${pageContext.request.contextPath}/action1.actionvalue: url標簽中的value用于輸出value的值, 此時的value后面的參數不是OGNL表達式, 只是一個普通字符串; 而param中的value代表get方式添加參數, 會自動編碼 如:username?test var: 將值為url作為key存入ActionMap中使用上面的url<a href="<s:property value='#url'>">點擊</a>當使用<s:a>標簽的時候就類似于使用<s:url>標簽與<a>標簽的結合品<s:a action="action1"><s:param name="username" value=" 'test' "></s:a>
- if/else標簽:
if/else標簽的用法與JSP中標簽的用法一致
<s:if test=" name=='A' ">A</s:if>
<s:elseif test=" name=='B' ">B</s:if>
<s:else>C
防止表單數據重復提交
Servlet中的解決方式:
- 用戶第一次提交表單數據之前先將數據存入Session域中, 當用戶數據提交之后就刪除Session數據, 當用戶重復提交的時候會發現表單中數據與Session中數據無法做比較, 最后判定數據重復提交, 對后面提交的數據不作處理
Struts2中的解決方式
- 使用token/tokenSession攔截器(Struts2提供的一個攔截器)
使用token/tokenSession攔截器的時候, 會產生一個令牌隱藏在Session域中, 當用戶提交數據后令牌消失, 下一次提交找不到對應的令牌, 所以最后判定為用戶數據重復提交
如上面看到的, Struts2中防止重復提交與Servlet中防止重復提交的思想是一致的此處說明一個問題就是:token/tokenSession不在Struts2的defaultStack中, 在使用的時候需要配置defaultStack攔截器token與tokenSession的區別:token做攔截的時候, 當用戶提交后有一個頁面跳轉, 有一個錯誤反饋, 告訴用戶數據重復提交tokenSession做攔截的時候, 不去做頁面跳轉和錯誤反饋, 無論用戶提交多少次表單數據, tokenSession只取第一次提交的數據
下面使用token舉例說明(使用tokenSession的時候, 只需將token改為tokenSession):
index.jsp:<s:form action="save"><s:token></s:token> <%此標簽用于生成隱藏令牌%><s:textfield name="name" label="用戶名"></s:textfield><s:submit value="保存"></s:submit></s:form>Struts.xml:<struts><constant name="struts.devMode" value="true"></constant><package name="p1" extends="struts-default"><action name="save" class="com.action.DemoAction"><interceptor-ref name="defaultStack"></interceptor-ref><!--配置token攔截器--><interceptor-ref name="token"></interceptor-ref><result type="redirect">/success.jsp</result><!--使用token的時候必須做invalid.token跳轉, 跳轉到error.jsp中--><!--在使用tokenSession的時候不需要做invalid處理--><result name="invalid.token">/error.jsp</result></action></package></struts>
到此Struts2的學習就結束了, 在后面的學習中, 我會將一個沒有講到的內容以及一些容易犯錯的地方補充上.
上面有錯, 還請指出, 如果認為我寫的還不錯, 還請點個贊, 多多支持一下, O(∩_∩)O~~
總結
以上是生活随笔為你收集整理的Struts2_4_ActionMap与ValueStack详解_Struct2的EL及常用标签_防止表单数据重复提交的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。