Struts2_ValueStack

在介绍什么是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属性:张三



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值