1、值栈
值栈: ValueStack
当浏览器访问action的时候,会被前端控制器(StrutsPrepareAndExecuteFilter)拦截住,在filter中创建值栈(ValueStack)对象(特点:访问一次,创建一次)
创建完以后,会将访问的整个action对象放在ValueStack中,还会将request,session,servletContext对象的底层用来存储数据的map数据集合放在ValueStack中,还会将当前页面提交的所有数据以map的方式也存放在ValueStack中(ps:存放的都是地址引用)
当整个action执行完毕,action对象会被销毁,ValueStack对象也会被销毁下次访问又是一个新的aciton对象和新的ValueStack对象,也就是说:aciton的生命周期和ValueStack是同步的,ValueStack会伴随action一生
ValueStack: 是struts2提供了一个接口 真正的实现类是:OgnlValueStack
值栈的内存区域由两部分组成,root区和context区,大致描述图如下:
从应用角度出发,值栈的主要作用是供Struts2的使用者通过值栈来存取数据。
那么,如何操作值栈中的数据,又如何获取值栈中的数据?
首先,如何操作值栈中的数据,我们可以通过ActionContext的对象来获取值栈对象,这是因为在Action的实例被创建时,会创建值栈对象,并将值栈对象存入ActionContext中。主要源码如下:
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
因此,要获取ValueStack对象,只需要通过ActionContext来获取:
ValueStack valueStack = ActionContext.getContext().getValueStack();
操作值栈中的数据分为两部分,一部分为操作root中的数据,一部分为操作context区中的数据
操作root区数据,可以在获取值栈对象后,调用set或者push方法,其中push会将数据存储在栈顶
public class ValueStack3Action extends ActionSupport{
@Override
public String execute() throws Exception {
// 获得值栈:
ValueStack valueStack = ActionContext.getContext().getValueStack();
/**
* ValueStack中的方法:
* * void set(String key,Object value); -- 创建一个Map集合,将Map集合存入到值栈(root)中。
* * void push(Object obj); -- 将对象存入到值栈(root)中。
* * 栈的特点:先进后出.
*/
valueStack.set("name", "王强勇");// 一般list集合使用set
User user = new User();
user.setUsername("aaa");
valueStack.push(user); // 一般Object使用push
return SUCCESS;
}
}
也可以通过想action类提供成员属性的方式来放入数据,上一篇说过,Struts2中的action为多实例,也就是其可以创建成员属性,而action对象会默认存储在root区的栈顶,只要对action加入成员属性,这个成员属性也会出现在栈中。
public class ValueStack3Action extends ActionSupport{
private Integer age;
public Integer getAge() {
return age;
}
..
}
操作context区的数据,主要是使用ActionContext的对象来调用get方法,获取context区中的Map,如Session,操作如下:
ActionContext act = ActionContext.getContext();
Map<String,Object> sessionMap = act.getSession();
sessionMap.put();
在往值栈中放入数据后,需要从页面获取到,实际上整个值栈中的数据都可以在页面获取到,可以用<s:debug></s:debug>来进行测试,但是要获取值栈中的数据,必须使用OGNL表达式,而在Struts2中,OGNL表达式必须在Struts2标签中使用,Struts2将OGNL表达式作为了自己的默认页面表达式语言。
要使用Struts2标签,必须首先在jsp页面中进行导入:<%@taglib prefix="s" uri="/struts-tags"%>,示例:
<h1>获取root区数据</h1>
<h3>获取push的数据</h3>
<s:property value="username"/>
<s:property value="password"/>
<h3>获取set的数据</h3>
<s:property value="user.username"/>
<s:property value="user.password"/>
<h3>获取list集合中的数据</h3>
<s:property value="list[0].username"/>
<s:property value="list[0].password"/>
<s:property value="list[1].username"/>
<s:property value="list[1].password"/>
<s:property value="list[2].username"/>
<s:property value="list[2].password"/>
<br/>
<h1>获取context区数据</h1>
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#parameters.id"/>
<s:property value="#attr.name"/>
最主要的需要记住,当获取root区中的数据时,不需要使用#,而context区的数据需要加#。
此外,也可以用el表达式获取值栈中的数据,11个常用的web操作对象都可以:
requestScope、sessionScope、pageScope、applicationScope、param、paramValues、header、headerValue、cookie、initparams、pageContext,举例如下:
${sessionScope.msg1 }
${applicationScope.msg2 }
${requestScope.msg }--->${msg}
2、OGNL表达式简介
OGNL表达式:对象图导航语言,是一门功能强大的表达式语言(功能比EL强大很多倍)。
ognl是单独的表达式语言,Struts2将OGNL引入到自身
所以:struts2的默认表达式语言就是ognl
OGNL的作用如下:
a、调用对象的方法(了解)
b、调用类的静态属性(了解)
条件: @全限定名@静态属性
c、调用类的静态方法(了解)
条件: @全限定名@静态方法
配置常量允许调用类的静态方法
d、获取值栈的数据
其中a-c的用法举例如下:
<h1>OGNL在Struts2环境中使用</h1>
<h3>访问对象的方法</h3>
hello的长度是:<s:property value="'helloworld'.length()"/>
<h3>访问对象的静态属性</h3>
π的值是:<s:property value="@java.lang.Math@PI"/><br/>
<h3>访问对象的静态方法</h3>
随机数是:<s:property value="@java.lang.Math@random()"/>
OGNL中的三个符号:#,%,$
#:获取context区中的数据,遍历list集合、遍历Map集合、构建Map集合,举例如下:
#获取context中的数据
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#parameters.id"/>
<s:property value="#attr.name"/>
<h3>遍历List集合</h3>
<s:iterator var="i" value="{'aa','bb','cc'}">
<s:property value="#i"/><br/>
</s:iterator>
<h3>遍历Map集合</h3>
<s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33'}">
<s:property value="key"/>--<s:property value="value"/><br/>
<s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
</s:iterator>
<h3>使用Struts2UI标签构建Map集合的地方</h3>
传统方式:<br/>
性别:<input type="radio" name="sex" value="男"/>男<input type="radio" name="sex" value="女"/>女<br/>
性别:<input type="radio" name="sex" value="1"/>男<input type="radio" name="sex" value="2"/>女<br/>
Struts2UI标签的方式:<br/>
<s:radio list="{'男','女'}" name="sex" label="性别"/>
<s:radio list="#{'1':'男','2':'女' }" name="sex" label="性别"/>
%:用于强制解析OGNL表达式(?)
<%
request.setAttribute("name", "王强勇");
%>
<input type="text" name="name" value="<s:property value="#request.name"/>"/>
<s:textfield name="name" value="%{#request.name}"/>
$符号:在Struts2的配置文件中使用,来进行取值
如:文件下载
<action name=”download” class=””>
<result name=”” type=”stream”>${fileName}</result>
</action>