Struts2_ValueStack

来源:互联网 发布:水果软件可以录歌吗 编辑:程序博客网 时间:2024/06/09 14:18

在介绍什么是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++;}@Overridepublic void clear() {this.top = null;this.size = 0;}@Overridepublic boolean isEmpty() {return this.top == null;}@Overridepublic T peek() {return this.top.getData();}@Overridepublic T pop() {T data = null;if (null == this.top) {return null;}data = this.top.getData();// 获取元素this.top = this.top.getNext();this.size--;return data;}@Overridepublic void push(T data) {Node<T> node = new Node<T>(data, top);this.top = node;this.size++;}@Overridepublic 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];}@Overridepublic void clear() {Arrays.fill(array, null);this.top = 0;this.size = 0;this.capacity = this.DEFAULT_SIZE;this.array = new Object[this.capacity];}@Overridepublic boolean isEmpty() {return size == 0;}@SuppressWarnings("unchecked")@Overridepublic T peek() {return (T) this.array[this.top - 1];}@SuppressWarnings("unchecked")@Overridepublic T pop() {T data = (T) this.array[this.top - 1];this.array[this.top - 1] = null;this.top--;this.size--;return data;}@Overridepublic 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;}@Overridepublic 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属性:张三



0 0