在介绍什么是ValueStack之前先介绍一下,如何自定义栈
首先定义统一栈接口:
/**
* cn.jbit.my.stack.IStack
* 栈接口
* 2014-4-1
* gyy
*/
package cn.jbit.my.stack;
public interface IStack<T> {
/**
* 元素压栈
* @param data 元素
*/
public void push(T data);
/**
* 元素出栈
* @return 栈元素
*/
public T pop();
/**
* 获取栈顶元素
* @return
*/
public T peek();
/**
* 判断栈是否为空
* @return true->空 false->非空
*/
public boolean isEmpty();
/**
* 清空
*/
public void clear();
/**
* 获取栈内元素个数
* @return 元素个数
*/
public int size();
}
方式一:
/**
* cn.jbit.my.stack.LinkedStack
* 链表实现栈
* 2014-4-16
* gyy
*/
package cn.jbit.my.stack;
public class LinkedStack<T> implements IStack<T> {
private Node<T> top;// 栈顶节点
private int size;// 元素个数
public LinkedStack() {
}
public LinkedStack(T data) {
this.top = new Node<T>(data);
this.size++;
}
@Override
public void clear() {
this.top = null;
this.size = 0;
}
@Override
public boolean isEmpty() {
return this.top == null;
}
@Override
public T peek() {
return this.top.getData();
}
@Override
public T pop() {
T data = null;
if (null == this.top) {
return null;
}
data = this.top.getData();// 获取元素
this.top = this.top.getNext();
this.size--;
return data;
}
@Override
public void push(T data) {
Node<T> node = new Node<T>(data, top);
this.top = node;
this.size++;
}
@Override
public int size() {
return this.size;
}
}
方式二:
package cn.jbit.my.stack;
import java.util.Arrays;
public class ArrayStack<T> implements IStack<T> {
private final int DEFAULT_SIZE = 3;
private int size = 0;
private int capacity = 0;
// top指向下一个能够添加元素的位置
private int top = 0;
private Object[] array;
public ArrayStack() {
this.capacity = this.DEFAULT_SIZE;
this.array = new Object[this.capacity];
}
public ArrayStack(int capacity) {
this.capacity = capacity;
this.array = new Object[this.capacity];
}
@Override
public void clear() {
Arrays.fill(array, null);
this.top = 0;
this.size = 0;
this.capacity = this.DEFAULT_SIZE;
this.array = new Object[this.capacity];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@SuppressWarnings("unchecked")
@Override
public T peek() {
return (T) this.array[this.top - 1];
}
@SuppressWarnings("unchecked")
@Override
public T pop() {
T data = (T) this.array[this.top - 1];
this.array[this.top - 1] = null;
this.top--;
this.size--;
return data;
}
@Override
public void push(T data) {
if (this.size < this.capacity) {
// 直接添加
this.array[this.top] = data;
this.top++;
this.size++;
} else {
// 扩展容量
enlarge();
push(data);
}
}
// 扩展容量
public void enlarge() {
this.capacity += this.DEFAULT_SIZE;
Object[] copy = new Object[this.capacity];
System.arraycopy(this.array, 0, copy, 0, this.array.length);
// array数组置空
Arrays.fill(this.array, null);
this.array = copy;
}
@Override
public int size() {
return this.size;
}
}
方式三:
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
数据传输背后机制:ValueStack(值栈)
在这一切的背后,是因为有了ValueStack(值栈)!
ValueStack基础:OGNL
要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)!
OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性;它用于界面将参数传递到Action(并进行类型转换)中;它还可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本机制。
Root对象
OGNL称为对象图导航语言。所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。不多说,直接演示代码:
package cn.jbit.my.ognl;
public class Organization {
private String orgId;
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
}
package cn.jbit.my.ognl;
public class Group {
private String name;
private Organization org;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Organization getOrg() {
return org;
}
public void setOrg(Organization org) {
this.org = org;
}
}
package cn.jbit.my.ognl;
public class User {
private String username;
private Group group;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public int addSomething(int numa, int numb) {
return numa + numb;
}
}
package cn.jbit.my.stack;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ognl.Ognl;
import ognl.OgnlException;
import cn.jbit.my.ognl.Group;
import cn.jbit.my.ognl.Organization;
import cn.jbit.my.ognl.User;
public class Demo2 {
public void testOgnl() throws OgnlException {
Organization org = new Organization();
org.setOrgId("ABC");
Group group = new Group();
group.setName("组1");
group.setOrg(org);
User user = new User();
user.setUsername("zhangsan");
user.setGroup(group);
// 利用OGNL表达式导航
String value = (String) Ognl.getValue("group.org.orgId", user);
System.out.println("值:" + value);
}
public void testOgnl2() throws OgnlException {
Organization org = new Organization();
org.setOrgId("ABC");
Group group = new Group();
group.setName("组1");
group.setOrg(org);
User user = new User();
user.setUsername("zhangsan");
user.setGroup(group);
// 利用OGNL表达式导航
// 在表达式中使用#root来代替root对象
String value = (String) Ognl.getValue("#root.group.org.orgId", user);
System.out.println("值:" + value);
}
public void testOgnl3() throws OgnlException {
Organization org = new Organization();
org.setOrgId("ABC");
Group group = new Group();
group.setName("组1");
group.setOrg(org);
User user = new User();
user.setUsername("zhangsan");
user.setGroup(group);
User user2 = new User();
user2.setUsername("li4");
Map<String, User> context = new HashMap<String, User>();
context.put("u1", user);
context.put("u2", user2);
/**
* 所谓context其实就是一个Map类型的对象。主要是因为在OGNL中,不支持多个root对象,那么
* 如果需要在表达式中访问更多毫不相干的对象时,只能通过一个Map来把这些对象统一传递给OGNL
*/
String value = (String) Ognl.getValue("#u1.username+','+#u2.username",
context, new Object());
System.out.println(value);
}
public void testOgnl4() throws OgnlException {
Organization org = new Organization();
org.setOrgId("ABC");
Group group = new Group();
group.setName("组1");
group.setOrg(org);
User user = new User();
user.setUsername("zhangsan");
user.setGroup(group);
User user2 = new User();
user2.setUsername("li4");
User user3 = new User();
user3.setUsername("w5");
Map<String, User> context = new HashMap<String, User>();
context.put("u1", user);
context.put("u2", user2);
// 给OGNL传递root对象及context对象,以便解释对应的表达式
String value = (String) Ognl.getValue(
"#u1.username+','+#u2.username+','+username", context, user3);
System.out.println(value);
}
// OGNL表达式赋值操作
public void testOgnl5() throws OgnlException {
User user = new User();
// 第一个参数:OGNL表达式
// 第二个参数:root对象
// 第三个参数:要赋的值
Ognl.setValue("username", user, "张三");
System.out.println("用户名:" + user.getUsername());
}
public void testOgnl6() throws OgnlException {
User user = new User();
Map<String, User> context = new HashMap<String, User>();
context.put("u", user);
// 第一个参数:OGNL表达式
// 第二个参数:context对象
// 第三个参数:root对象
// 第四个参数:要赋的值
Ognl.setValue("#u.username", context, new Object(), "zhangsan");
System.out.println("用户名:" + user.getUsername());
}
public void testOgnl7() throws OgnlException {
User user = new User();
Map<String, User> context = new HashMap<String, User>();
context.put("u", user);
// 利用赋值符号"="来进行赋值
// 注意:getValue方法
Ognl.getValue("#u.username = '李四'", context, new Object());
System.out.println("用户名:" + user.getUsername());
}
public void testOgnl8() throws OgnlException {
User user = new User();
User user2 = new User();
Map<String, User> context = new HashMap<String, User>();
context.put("u1", user);
context.put("u2", user2);
// 在一个表达式中可以用逗号分隔,同时执行多个表达式
Ognl.getValue("#u1.username = '张三',#u2.username = '李四'", context,
new Object());
System.out.println("用户名A:" + user.getUsername() + ",用户名B:"
+ user2.getUsername());
}
// --------------------------------OGNL调用对象的方法-------------------------------
public void testOgnl9() throws OgnlException {
User user = new User();
// 如果是调用root对象的方法,可以直接通过方法的名称来调用方法
Integer value = (Integer) Ognl.getValue("addSomething(1,1)", user);
System.out.println("值:" + value);
}
public void testOgnl10() throws OgnlException {
User user = new User();
user.setUsername("李四");
// 如果是调用root对象的方法,可以直接通过方法的名称来调用方法
String value = String.valueOf(Ognl.getValue("getUsername()", user));
System.out.println("用户名:" + value);
}
public void testOgnl11() throws OgnlException {
User user = new User();
// 通过调用root对象的方法赋值
Ognl.getValue("setUsername('李四')", user);
// 通过调用root对象的方法获取值
String value = String.valueOf(Ognl.getValue("getUsername()", user));
System.out.println("用户名:" + value);
}
// --------------------------------OGNL调用静态方法和变量-------------------------------
public void testOgnl12() throws OgnlException {
User user = new User();
user.setUsername("李四");
// 调用静态变量
// 注意:out是System中的静态变量,println()则是out这个对象内的实例方法(不是静态方法)
// 注意:需要在(静态)类名、变量名前面加@调用,对于实例方法 ,用"."直接调用
Ognl.getValue("@System@out.println(username)", user);
}
public void testOgnl13() throws OgnlException {
User user = new User();
user.setUsername("wangwu");
// 调用静态方法,注意使用全路径类名
Ognl
.getValue(
"@System@out.println(@cn.jbit.my.ognl.Utils@toUpperCase(username))",
user);
}
// 利用OGNL访问数组、集合对象
@SuppressWarnings("unchecked")
public void testOgnl14() throws OgnlException {
Object root = new Object();
Map<String, Object> context = new HashMap<String, Object>();
// 利用OGNL创建java.util.List对象
List<Object> list = (List<Object>) Ognl.getValue("{123,'xxx','abc'}",
context, root);
context.put("list", list);
// 利用OGNL创建数组
int[] intarray = (int[]) Ognl.getValue("new int[]{1,2,3}", context,
root);
context.put("intarray", intarray);
// 利用OGNL创建java.util.Map对象
Map<String, Object> map = (Map<String, Object>) Ognl.getValue(
"#{'listvalue':#list,'intvalue':#intarray}", context, root);
context.put("map", map);
// 利用OGNL表达式访问这些数组和集合对象
Ognl.getValue("@System@out.println(#list[1])", context, root);
Ognl.getValue("@System@out.println(#intarray[2])", context, root);
Ognl.getValue("@System@out.println(#map.listvalue[0])", context, root);
Ognl.getValue("@System@out.println(#map['listvalue'][0])", context,
root);
}
public static void main(String[] args) throws OgnlException {
Demo2 demo = new Demo2();
// demo.testOgnl();
// demo.testOgnl2();
// demo.testOgnl3();
// demo.testOgnl4();
// demo.testOgnl5();
// demo.testOgnl6();
// demo.testOgnl7();
// demo.testOgnl8();
// demo.testOgnl9();
// demo.testOgnl10();
// demo.testOgnl11();
// demo.testOgnl12();
// demo.testOgnl13();
demo.testOgnl14();
}
}
什么是ValueStack
Strut2的Action类通过属性可以获得所有相关的值,如请求参数属性值等。要获得这些参数值,我们要做的唯一一件事就是在Action类中声明与参数同名的属性。在Struts2调用Action类的Action方法(默认是execute方法)之前,就会为相应的Action属性赋值。要完成这个功能,有很大程度上,Struts2要依赖于ValueStack对象。这个对象贯穿整个Action的生命周期,每个Action类的对象实例会拥有一个ValueStack对象。当Struts2接收到一个.action的请求后,会先建立Action类的对象实例,但并不会调用Action方法,而是先将Action类的相应属性放到ValueStack对象的顶层节点(ValueStack对象相当于一个栈)。只是所有的属性值都是默认的值,如String类型的属性值为null,int类型的属性值为0等。在处理完上述工作后,Struts 2就会调用拦截器链中的拦截器,这些拦截器会根据用户请求参数值去更新ValueStack对象顶层节点的相应属性的值,最后会传到Action对象,并将ValueStack对象中的属性值,赋给Action类的相应属性。当调用完所有的拦截器后,才会调用Action类的Action方法。ValueStack会在请求开始时被创建,请求结束时消亡。
理解ValueStack的基本机制!对各种现象作出解释。
ValueStack实际上就是对OGNL的封装,OGNL主要的功能就是赋值与取值,Struts2正是通过ValueStack来进行赋值与取值的!
ValueStack是一个接口,而OgnlValueStack是strtus2中的缺省实现。ValueStack中的数据,分两个部分存放:root和context(这与OGNL中的概念一致),同时ValueStack暴露相关的接口:
void setValue(String expr, Object value);
Object findValue(String expr);
用来通过OGNL表达式对ValueStack中的数据进行操作!
ValueStack中的root对象是CompoundRoot,CompoundRoot继承了ArraryList,提供了额外的方法:push()和pop()方法,用来对root对象中所包含的数据进行存取!
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
正是通过这两个方法,CompoundRoot变成了一个栈结构!压栈操作,将导致对象被放到CompoundRoot的第0个元素上(第0个元素是栈顶),其它对象被依次往后移动;出栈操作,将导致CompoundRoot的第0个元素被移除(即栈顶元素被弹出),其它对象被依次往前移动!
OGNL不支持多个root对象,而struts2能够支持多个root对象,它对OGNL做了扩展。
如果某个OGNL表达式被传递给ValueStack(即调用ValueStack的setValue或findValue方法),而表达式中包含有对root对象的访问操作,ValueStack将依次从栈顶往栈底搜索CompoundRoot对象中所包含的对象,看哪个对象具有相应的属性,找到之后,立刻返回。
在Struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是CompoundRoot中的一个元素。看下面的代码:
package cn.jbit.my.action;
public class UserAction {
private String username;
private Integer age;
private boolean valid;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public String detail() {
username = "张三";
age = 18;
valid = true;
return "detail";
}
}
在Action中,给Action的username/age/valid赋值。Detail页面如下:
username:<s:property value="username"/> <br/>
valid:<s:property value="valid"/> <br/>
age:<s:property value="age"/> <br/>
上述JSP页面将能正确将它们的值取出。<s:property value=”ognl表达式”/>。在s:property标签中的OGNL表达式,最终会交给ValueStack来解释。username就是一个OGNL表达式,意思是调用root对象的getUsername()方法。Struts2将自动搜索CompoundRoot中有哪些元素(从第0个元素开始搜索),检测这些元素是否有getUsername()方法,如果第0个元素没有getUsername()方法,将继续搜索第1、2、3……个元素是否有getUsername()方法。
在上面的例子中,CompoundRoot中只有一个对象,就是userAction对象,而这个对象中正好有getUsername()方法,所以,上述JSP代码将能够将值正确取出。
再看下面的例子:
package cn.jbit.my.action;
import com.opensymphony.xwork2.ActionContext;
import cn.jbit.my.ognl.User;
public class UserAction2 {
private String username;
private String name;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String detail() {
username = "张三";
name = "王五";
User u = new User();
u.setUsername("赵毅");
ActionContext.getContext().getValueStack().push(u);
return "detail";
}
}
在上面这个UserAction的代码中,我们直接调用ActionContext.getContext().getValueStack().push()方法,把一个User对象(这个对象拥有getUsername()和setUsername()方法)直接压入到ValueStack中,这时候,在ValueStack的CompoundRoot中将有两个元素:第0个元素是刚刚压入的user对象[赵毅],而第1个元素是userAction对象[张三],如果在JSP中使用下面的表达式来取值:
<s:property value=”username”/> ,那么输出的值将是“赵毅”!道理上面已经讲过了,struts2将会从第0个元素开始搜索CompoundRoot中的对象,第0个元素正是刚刚压入的那个user对象!
如果在JSP中使用<s:property value=”name”/>来取值,将取出“王五”,因为第0个元素user对象没有name属性,所以,会继续搜索第1个元素userAction对象,在这个对象中就有name属性了!
再看下面的代码:
package cn.jbit.my.action;
import java.util.ArrayList;
import java.util.List;
import cn.jbit.my.ognl.User;
import com.opensymphony.xwork2.ActionContext;
public class UserAction3 {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String detail() {
username = "张三";
List<User> list = new ArrayList<User>();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setUsername("User" + i);
list.add(user);
}
ActionContext.getContext().put("users", list);
User u = new User();
u.setUsername("赵毅");
ActionContext.getContext().getValueStack().push(u);
return "detail";
}
}
对应的JSP如下:
<s:property value="username"/> <br/>
<s:iterator value="#users">
<s:property value="username"/>
<s:property value="#root[2].username"/><br/>
</s:iterator>
<s:property value="username"/>
<s:property value="#root[1].username"/> <!-- 张三 -->
根据刚才的示例,我们知道,第1行的username是“赵毅”(因为JSP在执行这行代码的时候,CompoundRoot中有两个元素:第0个是“user对象赵毅”,第1个是“userAction对象张三”),因此第1行的username将取出CompoundRoot中第0个元素的username属性:赵毅
第2行代码是iterator标签,只定义了一个value属性,iterator标签将循环访问users这个List中的User对象,并把当前循环的user对象压入到CompoundRoot中!所以,在第3行和第4行代码被执行的时候,CompoundRoot中总共有3个元素:第0个元素是被iterator标签压入的当前循环的user对象;第1个元素是“user对象赵毅”;第2个元素是“userAction对象张三”,因此第3行代码的执行结果就是输出“UserX”,即当前循环的user对象的username属性!iterator标签将会依次取出List中的user对象,并不断压入/弹出user对象(每次循环,都将执行一遍压入/弹出)。而第4行代码取第2个元素的username属性,即userAction对象的username属性:张三。
第5行代码执行完成之后,在CompoundRoot中将剩下2个元素,与第2行代码被执行之前一样。所以,第6行代码的输出和第1行代码的输出结果是一样的,而第7行代码将取出userAction对象的username属性:张三