概述
值栈就相当于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。
另外,root栈中的值只能在请求转发中取出,无法在重定向后取出,而context栈中的值在请求转发或者重定向都可以取出。
注意:
在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 />
可以查看值栈的内容
除此之外,我们也可以使用el表达式来取值。
获取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.youkuaiyun.com.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"/>
<!-- 或者直接使用el表达式获取 -->
${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"/>
<!-- 或者直接使用el表达式获取 -->
${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"/>
<!-- 或者直接使用el表达式获取 -->
${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.youkuaiyun.com.mchenys.DownloadAction">
<result name="success" type="stream">
<param name="contentType">${contentType}</param>
<param name="contentDisposition">attachment;filename=${downFilename}</param>
</result>
</action>