文章目录
1. OGNL表达式
1.1 什么是OGNL表达式
在讲解值栈之前我们先来了解一下OGNL表达式,OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取Java对象的任意属性,调用Java对象的方法,同时能够自动实现必要的类型转换。如果把表达式看作是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。
OGNL不是struts2的一部分,而是一个单独的项目,经常和struts2一起使用,常和struts2标签一起使用操作值栈。
1.2 OGNL表达式使用案例
在jsp中使用OGNL表达式:
<!-- 使用 struts2标签时候,在 jsp中引入标签库 -->
<%@ taglib uri="/struts-tags" prefix="s"%>
<!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>
<!-- 使用 ognl+struts2标签实现计算字符串长度
value属性值:ognl表达式
-->
<s:property value="'joker'.length()"/>
</body>
</html>
在Action中使用OGNL表达式:
@Test
public void test1() throws OgnlException{
OgnlContext context = new OgnlContext();
Object obj = Ognl.getValue("'joker'.length()", context, context.getRoot());
System.out.println(obj);
}
2. 值栈
2.1 什么是值栈
在不使用MVC框架的web开发阶段,我们常在servlet里面进行操作,把处理完成的数据放到域对象里面,在页面中使用EL表达式获取到,利用域对象在一定范围内进行存值和取值。在struts2里面提供本身一种存储机制,类似于域对象,叫做值栈,也可以存值和取值,在Action里面把数据放到值栈里面,在页面中获取到值栈数据。
那么ValueStack是由谁创建的,又是什么时候创建的:
- 当浏览器访问Action的时候,会被前端控制器StrutsPrepareAndExecuteFilter拦截住,在filter中创建值栈(ValueStack)对象,特点是访问一次,创建一次。
- 创建完以后,会把当前的Action对象自动放入ValueStack中,还会将request、session、servletContext对象的底层用来存储数据的map数据集合放在ValueStack中,还会将当前页面提交的所有数据以map的方式也存放在ValueStack中(存放的都是地址引用)。
- 当整个Action执行完毕,Action对象会被销毁,ValueStack对象也会被销毁,下次访问,又是一个新的Action对象和新的ValueStack对象。也就是说,Action的生命周期和ValueStack是同步的,ValueStack会伴随Action一生。
ValueStack只是struts2提供了一个接口,真正的实现类是OgnlValueStack。
2.2 ActionContext和ValueStack的关系
当浏览器访问Action的时候,会创建值栈对象,还会创建ActionContext对象,最后把整个值栈放在ActionContext中,然后把整个ActionContext与当前线程绑定。下面为二者的关系:
- ActionContext在访问时候创建,每次访问Action类的时候,都会创建ActionContext对象。可以创建多次,多实例对象。
- 在创建ActionContext的时候会创建ValueStack的对象,将ValueStack对象赋值给ActionContext,在每个ActionContext对象里面都会有一个值栈对象。
- ActionContext中有一个ValueStack的引用,ValueStack中也有一个ActionContext的引用。
- ActionContext获取Servlet API的时候,就依赖值栈了。
2.3 获取值栈对象
//通过 ActionContext对象获取值栈
ValueStack valueStack = ActionContext.getContext().getValueStack();
//通过 request域获取值栈
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
2.4 值栈的内部结构
值栈包括两部分:
- root栈:继承了List接口,又称之为对象栈。
- context栈:实现了Map接口,又可以称之为上下文栈。
Struts会默认把下面这些映射压入context栈中:
- request:HttpServletRequest对象的引用。
- session:HttpSession对象的引用。
- application:ServletContext对象的引用。
- parameters:该Map中包含当前请求的请求参数。
- attr:该Map按如下顺序来检索某个属性:request、session、application。
2.5 操作值栈的root区
ValueStack的API的方式:
public class Demo1Action extends ActionSupport {
public String execute() throws Exception {
ActionContext context = ActionContext.getcontext();
ValueStack valueStack = context.getValueStack();
// valueStack的 push方法
valueStack.push("joker");
// valueStack的 set方法
valueStack.set("username", "joker");
return SUCCESS;
}
}
<!-- 通过 Map的 key可以直接获取 value值,在值栈中从上往下进行查找,找到的第一个进行返回 -->
<s:property value="username" />
<!-- 使用 push方法设置值,没有名称,只有设置的值,所以向值栈放数据时,
把向值栈放数据存到数组里面,数组名称 top,根据数组获取值,[0].top时可以省略
-->
<s:property value="[1].top" />
Action的属性方式:
public class Demo2Action extends ActionSupport {
//都放在 root区了,因为整个 Demo2Action都在 root区中
private String str;
private Student student;
private List<User> list = new Arraylist<User>();
public String getStr(){
return str;
}
public User getStudent() {
return student;
}
public List<User> getList() {
return list;
}
public String execute() throws Exception {
str = "this is a test";
student.setName("joker");
student.setAge(18);
User user1 = new User();
user.setUsername("zhangsan");
user.setAge(18);
user.setBirthday(new Date());
User user2 = new User();
user.setUsername("lisi");
user.setAge(20);
user.setBirthday(new Date());
list.add(user1);
list.add(user2);
return "success";
}
}
<!-- 基本数据类型 -->
<s:property value="str"/>
<!-- 对象 -->
<s:property value="student.name" />
<s:property value="student.age" />
<!-- 集合遍历方式一 -->
<s:property value="list[0].username" />
<s:property value="list[0].age" />
<s:property value="list[0].birthday" />
<s:property value="list[1].username" />
<s:property value="list[1].age" />
<s:property value="list[1].birthday" />
<!-- 集合遍历方式二
* value 要迭代的集合,需要从值栈中获取
* var 迭代过程中,遍历的对象
var不编写,默认把迭代产生的对象压入到 root栈中
var编写上,把迭代产生的对象默认压入到 context栈中,从 context栈取值,需要加 #号
-->
<s:iterator value="list" >
<s:property value="user.username" />
<s:property value="user.age" />
<s:property value="user.birthday" />
</s:iterator>
<s:iterator value="list" var="user" >
<s:property value="#user.username" />
<s:property value="#user.age" />
<s:property value="#user.birthday" />
</s:iterator>
2.6 操作值栈的context区
public class Demo3Action extends ActionSupport {
public String execute() throws Exception {
ActionContext context = ActionContext.getContext();
Map<String, Object> map = context.getSession();
map.put("msg1", "sessionMessage");
Map<String, Object> map2 = context.getApplication();
map2.put("msg2", "servletContextMessage");
context.put("msg3", "requestMessage");
return SUCCESS;
}
}
<s:property value="#session.msg1"/>
<s:property value="#application.msg2"/>
<s:property value="#request.msg3"/> <s:property value="#msg3"/>
<s:property value="#attr.msg2"/>
2.7 为什么EL也能访问值栈中的数据
StrutsPreparedAndExecuteFilter的doFilter代码中request = prepare.wrapRequest(request);代码对Request对象进行了包装,StrutsRequestWrapper增强了request的getAttribute方法。首先从request域获取值,如果获取到,直接返回。如果从request域获取不到值,到值栈中把值获取出来,把值放到域对象里面。
3 OGNL表达式的特殊的符号用法
-
#号的用法:
获得context栈中的数据:public class Demo7Action extends ActionSupport { public String execute() throws Exception { ActionContext context = ActionContext.getContext(); context.put("username", "joker"); return SUCCESS; } }
<s:property value="#request.username"/> <!-- 或 --> <s:property value="#username"/>
构建一个map集合:
<s:radio name="sex" list="{'男','女'}"></s:radio> <s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>
-
%号的用法
强制字符串解析成OGNL表达式:<!-- 在 request域中存入值,然后在文本框 <s:textfield>中显示存入的值 --> <s:textfield value="%{#request.msg}"/> <!-- {}中值用''引起来,此时不再是 ognl表达式,而是普通的字符串 --> <s:property value="%{'#request.msg'}"/>
-
$号的用法
在配置文件中可以使用OGNL表达式:<action name="download" class="cn.joker.demo8.DownloadAction"> <result name="success" type="stream"> <param name="contentType">${contentType}</param> <param name="contentDisposition">attachment;filename=${downFilename}</param> </result> </action>