九、Struts2的值棧
概述
值棧就相當於Struts2框架的資料的中轉站,向值棧存入一些資料。從值棧中獲取到資料。
ValueStack 是 struts2 提供一個介面,實現類 OgnlValueStack ---- 值棧物件 (OGNL是從值棧中獲取資料的 )。
Action是多例的,有多少請求,就建立多少Action例項,每一個Action會建立一個ActionContext物件,代表的是Action的上下文物件,同時還會建立一個ValueStack物件。
每個Action例項都有一個ValueStack物件 (一個請求 對應 一個ValueStack物件 ),在其中儲存當前Action 物件和其他相關物件
Struts 框架把 ValueStack 物件儲存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧物件 是 request一個屬性)
上面概述中提到了OGNL,這裡做下解釋
OGNL是Object Graphic Navigation Language(物件圖導航語言)的縮寫, 所謂物件圖,即以任意一個物件為根,通過OGNL可以訪問與這個物件關聯的其它物件,通過它簡單一致的表示式語法,可以存取物件的任意屬性,呼叫物件的方法,遍歷整個物件的結構圖,實現欄位型別轉化等功能。它使用相同的表示式去存取物件的屬性;
Struts2框架使用OGNL作為預設的表示式語言,OGNL是一種比EL強大很多倍的語言,Struts2的核心包中已經包含ognl的jar包,例如ognl-3.0.6.jar.
值棧的內部結構
值棧由兩部分組成,root棧和context棧
- root: Struts把動作和相關物件壓入ObjectStack 中, 內部是ArrayList資料結構。
- context :Struts把各種各樣的對映關係(一些 Map 型別的物件) 壓入ContextMap中,內部是Map資料結構,
Struts2會預設把下面這些對映壓入ContextMap(context)中:- parameters: 該 Map 中包含當前請求的請求引數 ?name=xxx&password=123
- request: 該 Map 中包含當前 request 物件中的所有屬性
- session: 該 Map 中包含當前 session 物件中的所有屬性
- application:該 Map 中包含當前 application 物件中的所有屬性
- attr: 該 Map 按如下順序來檢索某個屬性: request, session, application
其中parameters、request、session、application和attr將作為context棧中的Map的key。
注意:
在jsp頁面中使用OGNL表示式取值的時候,root棧和contex棧的取值有點不同訪問root棧中資料時,不需要寫#
訪問context棧中的request、 session、application、 attr(從最小域開始搜尋)、 parameters(請求引數) 物件資料必須寫 #
值棧和ActionContext物件的關係
值棧物件是請求時建立的, ActionContext是繫結到當前的執行緒上,那麼在每個攔截器或者Action中獲取到的ActionContext是同一個。
通過ActionContext可以獲取到值棧物件(ValueStack),例如:
ValueStack vs = ActionContext.getContext().getValueStack();
root棧儲存資料
ValueStack提供2個方法儲存root棧的資料
1.push方法
valueStack.push(Object obj);
push方法把資料儲存到root棧的棧頂位置,root棧是一個ArrayList集合,push方法原始碼如下:
public void push(Object o) {
root.push(o); //其中root是一個CompoundRoot,它是繼承ArrayList的
}
2.set方法
valueStack.set(String key, Object obj);
set方法儲存資料會先將資料儲存到內部的map集合,map的key就是該方法引數的key, map的value就是該方法引數的obj, 然後再將該map集合新增到root棧的棧頂位置,原始碼如下:
/**
* @see com.opensymphony.xwork2.util.ValueStack#set(java.lang.String, java.lang.Object)
*/
public void set(String key, Object o) {
Map setMap = retrieveSetMap(); //獲取一個內部的map集合
setMap.put(key, o);//儲存資料
}
private Map retrieveSetMap() {
Map setMap;
Object topObj = peek();//獲取棧頂的資料
if (shouldUseOldMap(topObj)) { //判斷棧頂資料是否是map集合
setMap = (Map) topObj;//是則強轉為map
} else {
setMap = new HashMap(); //不是則new一個map
setMap.put(MAP_IDENTIFIER_KEY, "");
push(setMap);//通過push方法,將map儲存到roo棧的棧頂
}
return setMap;//返回該map的引用
}
/**
* check if this is a Map put on the stack for setting if so just use the old map (reduces waste)
*/
private boolean shouldUseOldMap(Object topObj) {
return topObj instanceof Map && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null;
}
提示:
root棧中存入物件的話,優先使用push方法;存入集合的話,優先要使用set方法。
context棧儲存資料
valueStack沒有提供直接操作context棧的方法,但是可以通過其他物件來將資料儲存到context棧中,例如上面提到的request/session/application域物件,呼叫其setAttribute方法,其實就是將資料儲存到context棧中.
獲取值棧中的資料
Struts2引入了OGNL表示式,主要是在JSP頁面中獲取值棧中的值,首先需要先引入Struts2的標籤庫
<%@ taglib prefix="s" uri="/struts-tags" %>
然後使用Struts2提供的標籤中的標籤
<s:property value="OGNL表示式"/>
如果要訪問物件方法也是可以的,例如:
<s:property value="'hello'.length()"/>
在jsp中 通過<s:debug />
可以檢視值棧的內容
獲取root棧的資料
1.獲取push方法儲存的資料
package blog.csdn.net.mchenys;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo1Action extends ActionSupport {
private static final long serialVersionUID = 1L;
public String demo1() {
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.push(new User("mChenys",22));//儲存資料到root棧
return SUCCESS;
}
}
struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="*" class="blog.csdn.net.mchenys.Demo1Action" method="{1}">
<result name="success">/suc.jsp</result>
</action>
</package>
</struts>
jsp頁面取資料
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 匯入struts標籤庫 -->
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 從root棧中取資料
[0]:從root棧(ArrayList集合)中取資料,資料從0下標開始到最後一個數據位置為止
[0].top:拿到root棧頂的資料即第一條資料,即User物件
[0].top.username:通過user物件的username屬性獲取其值
注意:[0].top預設可以省略不寫
-->
<s:property value="[0].top.username"/>
</body>
</html>
2.獲取set方法儲存的資料
package blog.csdn.net.mchenys;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo1Action extends ActionSupport {
private static final long serialVersionUID = 1L;
public String demo2() {
ValueStack valueStack = ActionContext.getContext().getValueStack();
//儲存map集合
Map<String, User> map = new HashMap<>();
map.put("one", new User("zhangsan", 20));
map.put("two", new User("lisi", 22));
map.put("three", new User("wangw", 23));
valueStack.set("umap", map);
//儲存list集合
List<User> list = new ArrayList<>();
list.add(new User("tom", 30));
valueStack.set("ulist", list);
return SUCCESS;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 匯入struts標籤庫 -->
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 從root棧中獲取map物件中的資料
由於root棧呼叫set方法,會先將資料新增到一個內部的map集合,然後再將該map集合新增到內部的list集合
因此:如果外面傳入的是map的話,其應該是這樣的List<Map<umap,Map<key,value>>>
[0]:從root棧(ArrayList集合)中取資料,資料從0下標開始取,取到最後一個數據位置為止
[0].top:拿到root棧頂的資料即第一條資料,該條資料是一個內部的map集合,key=umap, value=資料(map物件)
[0].top.umap:通過key=umap可以拿到資料,而這裡的資料又是一個map物件
[0].top.umap.one:通過資料的map的key=one拿到user物件
[0].top.umap.one.username:通過user物件的username屬性獲取其值
注意:[0].top預設可以省略不寫
-->
<s:property value="[0].top.umap.one.username"/>
<!-- 從root棧中取list物件中的資料
[0]:從root棧(List集合)中取資料,資料從0下標開始取,取到最後一個數據位置為止
[0].top:拿到root棧頂的資料即第一條資料,該條資料是一個內部的map集合,key=ulist, value=資料(list物件)
[0].top.ulist:通過key=ulist可以拿到資料,資料是一個list物件
[0].top.ulist[0]:去list集合中第一條資料,即User物件
[0].top.ulist[0].username:通過user物件的username屬性獲取其值
注意:[0].top預設可以省略不寫
-->
<s:property value="[0].top.ulist[0].username"/>
</body>
</html>
獲取context棧的資料
和context棧存資料一樣只能通過特定的物件來獲取資料,獲取context棧的資料要使用#來標識,一共有以下物件可以獲取context域中儲存的資料:
request域 :<s:property value="#request.username"/>
session域 : <s:property value="#session.username"/>
application域 : <s:property value="#application.username"/>
attr(最小域開始搜尋) : <s:property value="#attr.username"/>
parameters(請求引數) : <s:property value="#parameters.cid"/>
例如:
package blog.csdn.net.mchenys;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo1Action extends ActionSupport {
private static final long serialVersionUID = 1L;
public String demo3() {
HttpServletRequest request = ServletActionContext.getRequest();
// 存單個物件
request.setAttribute("user", new User("tom", 30));
request.getSession().setAttribute("user", new User("jack", 22));
// 儲存list集合
List<User> list = new ArrayList<>();
list.add(new User("tom", 30));
list.add(new User("jack", 22));
ServletActionContext.getServletContext().setAttribute("ulist", list);
return SUCCESS;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 匯入struts標籤庫 -->
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 獲取域中的資料 -->
<s:property value="#request.user.username" />
<s:property value="#session.user.username" />
<s:property value="#application.ulist[0].age" />
<!-- 從最小域~最大域中獲取資料 -->
<s:property value="#attr.user.username"/>
<!-- 獲取請求引數 例如:http://localhost:8080/Struts2_03/demo3?id=3中的id-->
<s:property value="#parameters.id" />
</body>
</html>
迭代的標籤
通過<s:iterator></s:iterator>
標籤可以遍歷值棧中的集合資料,常用的屬性有value和var:
value:要迭代的集合,需要從值棧(root棧或者context棧)中獲取
var : (可選引數) 表示迭代過程中遍歷出來的物件
注意:
如果寫了var,那麼會把迭代產生的物件預設壓入到context棧中,從context棧取值要加#號;
如果不寫var,預設把迭代產生的物件壓入到root棧中
無論是壓入到哪個棧,取的時候每取出一個就會彈出棧一個.
package blog.csdn.net.mchenys;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class Demo1Action extends ActionSupport {
private static final long serialVersionUID = 1L;
public String demo4() {
//儲存集合到root棧
ValueStack valueStack = ActionContext.getContext().getValueStack();
List<User> list = new ArrayList<>();
list.add(new User("tom", 30));
list.add(new User("jack", 22));
valueStack.set("ulist", list);
//儲存集合到context棧
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("ulist", list);
return SUCCESS;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 匯入struts標籤庫 -->
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 從root棧中取資料 -->
<!-- 方式一 ,迭代的資料再次存入root棧中-->
<s:iterator value="ulist">
<!-- 從root棧頂取資料,每取一個,彈棧一個-->
<s:property value="username" />
<s:property value="age" />
</s:iterator>
<!-- 方式二 ,迭代的資料再次存入context棧中-->
<s:iterator value="ulist" var="u">
<!-- 從context棧頂取資料,每取一個,彈棧一個-->
<s:property value="#u.username" />
<s:property value="#u.age" />
</s:iterator>
<hr/>
<!-- 從context棧中取資料 -->
<!-- 方式一,迭代的資料再次存入root棧中 -->
<s:iterator value="#request.ulist">
<s:property value="username" />
<s:property value="age" />
</s:iterator>
<!-- 方式二,迭代的資料再次存入context棧中 -->
<s:iterator value="#request.ulist" var="u">
<s:property value="#u.username" />
<s:property value="#u.age" />
</s:iterator>
</body>
</html>
更多的標籤,可以檢視struts-2.3.24-all\struts-2.3.24\docs\docs\tag-reference.html文件說明
Struts2的2個特殊符號
-
% 符號的用法
強制字串解析成OGNL表示式。
例如:在request域中存入值,然後在文字框<s:textfield>
(Struts2的文字看標籤)中取值
<s:textfield value="%{#request.msg}"/>
注意: { }中值用’'引起來,此時不再是ognl表示式,而是普通的字串
例如:<s:property value="%{'#request.msg'}"/>
-
$ 符號的用法
在配置檔案中可以使用OGNL表示式,例如:檔案下載的配置檔案。
<action name="download1" class="blog.csdn.net.mchenys.DownloadAction">
<result name="success" type="stream">
<param name="contentType">${contentType}</param>
<param name="contentDisposition">attachment;filename=${downFilename}</param>
</result>
</action>