1. 程式人生 > >九、Struts2的值棧

九、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個特殊符號

  1. % 符號的用法
    強制字串解析成OGNL表示式。
    例如:在request域中存入值,然後在文字框<s:textfield>(Struts2的文字看標籤)中取值
    <s:textfield value="%{#request.msg}"/>

    注意: { }中值用’'引起來,此時不再是ognl表示式,而是普通的字串
    例如:<s:property value="%{'#request.msg'}"/>

  2. $ 符號的用法
    在配置檔案中可以使用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>