Struts2入门第八讲——值栈(ValueStack)

在前一讲中,我们已经入门了OGNL,我也说过使用OGNL表达式主要做的事情就是在Struts2里面获取值栈中的数据,本讲就是围绕这个而展开的。这里我给大家提个醒:本讲和上一讲是一脉相承的关系,我本意是想在上一讲中一块介绍OGNL和值栈这两个知识点,没想到文章越写越多,为了让大家方便观看,索性我就一分为二,在上一讲中介绍OGNL,在本讲中介绍值栈,所以,本讲中的一些案例都是建立在上一讲的基础之上的。

值栈的概述

什么是值栈(ValueStack)?

ValueStack是Struts2的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求,Struts2框架会创建一个Action实例,同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个Action的生命周期,Struts2中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。说白了,Valuestack其实类似于一个数据中转站,Struts2框架当中的数据都保存到了Valuestack中。
咱之前学习过,比如说在Servlet中要把数据传递到页面中显示,那么就要在Servlet里面把数据放到域对象里面,然后在jsp页面中使用EL表达式获取域对象里面的值。但是在Struts2框架这儿,就有很大的不同了,它里面提供了一个叫值栈的东西,它有点类似于域对象,值栈应用在Struts2的Action类里面,在值栈中咱可以存值和取值。
值栈的简单概念了解完之后,咱还得更加深入地挖一挖它内部的结构,不然只知其一,浮于表面,终究不过一丘之貉。在讲这个知识点之前,我们得清楚Servlet和Action之间的区别(之前,我记得说过一遍了):

  • Servlet默认在第一次访问的时候创建,而且只会创建一次,可以理解为它是单实例对象;
  • Action在访问的时候创建,每次访问Action的时候都会创建一个Action对象,可以理解为它是多实例对象。

值栈的内部结构

通过查看OgnlValueStack类的源码,可发现在OgnlValueStack类中包含两部分,值栈和Map(即OGNL上下文)。
在这里插入图片描述
下面,我会详细地说明OgnlValueStack类中root和context这俩变量。

  • context:即OgnlContext上下文,它是一个Map集合,在上下文中存储了一些引用,如parameters、request、session、application等,上下文的Root对象为CompoundRoot。OgnlContext中存储了一些如下的引用:
    • parameters:该Map中包含当前请求的请求参数;
    • request:该Map中包含当前request对象中的所有属性;
    • session:该Map中包含当前session对象中的所有属性;
    • application:该Map中包含当前application对象中的所有属性;
    • attr:该Map按这样的顺序(request、session、application)来检索某个属性。
  • root:它里面存储了Action实例,可作为OgnlContext的Root对象。除此之外,CompoundRoot继承ArrayList实现了压栈和出栈的功能,它拥有栈的特点(先进后出,后进先出),最后压进栈的数据在栈顶,我们亦可把它称为对象栈。

Struts2对原OGNL作出的改进就是Root使用的是CompoundRoot(自定义栈),使用OgnlValueStack类的findValue方法可以在CompoundRoot中从栈顶向栈底查找到对象的属性值。CompoundRoot作为OgnlContext的Root对象,并且在CompoundRoot中Action实例位于栈顶,当读取Action的属性值时会先从栈顶对象中找对应的属性,如果找不到则继续找栈中的其他对象,如果找到则停止查找。
口说无凭,这里我会通过一个案例来分析一下值栈的内部结构。首先,在src目录下新建一个com.meimeixia.struts2.valuestack包,并在该包下创建一个Action类(ValueStackDemo01.java)。

package com.meimeixia.struts2.valuestack;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * ValueStack的内部结构
 * @author liayun
 *
 */
public class ValueStackDemo01 extends ActionSupport {

	@SuppressWarnings("unused")
	@Override
	public String execute() throws Exception {
		//获取到值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		
		return NONE;
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    		</action>
    	</package>
    </struts>
    

最后,在ValueStackDemo01类中获取值栈对象的代码处打上一个断点,以断点模式来启动Tomcat服务器,立马在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo01.action这样的URL地址进行访问,这时Watch一下valueStack值,你将会看到如下结果:
在这里插入图片描述
其实,在Struts2的标签里面有一个标签可以看到值栈的内部结构,这个标签就是<s:debug>。这里,你只须这样做。首先,你得修改一下ValueStackDemo01类的代码。

package com.meimeixia.struts2.valuestack;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * ValueStack的内部结构
 * @author liayun
 *
 */
public class ValueStackDemo01 extends ActionSupport {

	@SuppressWarnings("unused")
	@Override
	public String execute() throws Exception {
		//获取到值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		
//		return NONE;
		return SUCCESS;
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    	</package>
    </struts>
    

紧接着,在WebContent/demo01目录下新建一个success.jsp页面。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ 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>
	<h1>查看值栈的内部结构</h1>
	<s:debug></s:debug>
</body>
</html>

最后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo01.action这样的URL地址进行访问,你就可以看到如下界面了。
在这里插入图片描述
点击上面那个超链接即可看到值栈结构,如下图所示:
在这里插入图片描述

小结

这里对值栈的内部结构做一个小结,我还是画个图吧!这样会更好点。
在这里插入图片描述
所说的操作值栈,通常指的是操作ValueStack中的root区域。

值栈与ActionContext的关系

ServletContext指的是Servlet的上下文,而ActionContext指的是Action的上下文。通过查阅StrutsPrepareAndExecuteFilter类的源码,可以看到当请求过来的时候,会执行过滤器中的doFilter方法,在这个方法中会创建ActionContext,如下图所示。
在这里插入图片描述
在创建ActionContext过程中,就会创建ValueStack对象,并且将ValueStack对象传递给了ActionContext对象,所以我们可以通过ActionContext来获取值栈对象。
在这里插入图片描述
这也能解释为什么通过ActionContext类能够访问Servlet的API(其实访问的是域对象中的数据,而不是真实的对象),因为在其内部有值栈的引用。

获取值栈对象

获取值栈对象有两种方式,第一种方式是使用ActionContext类,得到ActionContext对象,然后再使用ActionContext对象里面的方法得到值栈对象。为了演示这种方式,我们可以在com.meimeixia.struts2.valuestack包中创建一个Action类(ValueStackDemo02.java)。
在这里插入图片描述
第二种方式是通过request对象来获得。因为在Struts2框架的内部,还将值栈存入到了request对象中一份。这可以通过查看StrutsPrepareAndExecuteFilter类的源码知晓!
在这里插入图片描述
温馨提示:一个Action的实例,只会创建一个ValueStack的对象,也即一个ValueStack对象只会为一个Action实例服务。如果要证明这点,可以将ValueStackDemo02类的代码修改成下面这样子。

package com.meimeixia.struts2.valuestack;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * 获得值栈对象
 * 
 * 
 */
public class ValueStackDemo02 extends ActionSupport {

	@Override
	public String execute() throws Exception {
		//第一种方式获得值栈对象:通过ActionContext对象获得
		ValueStack valueStack1 = ActionContext.getContext().getValueStack();
		
		//第二种方式获得值栈对象:通过request对象来获得
		//ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valuestack");
		ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
		
		//一个Action的实例,只会创建一个ValueStack的对象,也即一个ValueStack对象只会为一个Action实例服务。
		System.out.println(valueStack1 == valueStack2);
		return NONE;
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo02" class="com.meimeixia.struts2.valuestack.ValueStackDemo02"></action>
    	</package>
    </struts>
    

最后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo02.action这样的URL地址,可以看到Eclipse控制台输出结果为true。这也就说明了在一个Action对象对应着一个值栈对象。

向值栈中保存数据

向值栈中放入数据,常用的有两种方式,它们分别是:

  • 第一种方式:在Action成员变量位置定义变量,然后提供这个变量的get方法。这是因为在默认的情况下,会将Action实例压入到值栈的栈顶位置,那么Action实例中的属性呢?也会在值栈中;
  • 第二种方式:获取值栈对象,调用值栈对象本身的方法,例如set方法和push方法。

不过在实际开发中,我们一般都使用的是第一种方式。但是上面的这两种方式,我们都应该重点掌握。

向值栈中存入Java对象

下面我分别用以上两种方式来演示如何向值栈中存入Java对象。

第一种方式:在Action成员变量位置定义变量,然后提供这个变量的get方法

首先,在com.meimeixia.struts2.valuestack包中创建一个Action类(ValueStackDemo03.java)。

package com.meimeixia.struts2.valuestack;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 操作ValueStack方式一:利用Action本身在值栈中的特性来实现的
 * @author liayun
 *
 */
public class ValueStackDemo03 extends ActionSupport {
	
	private User user;
	
	public User getUser() {
		return user;
	}

//	public void setUser(User user) {
//		this.user = user;
//	}

	@Override
	public String execute() throws Exception {
		//向ValueStack中存值
		user = new User("张敏", "123");
		return SUCCESS;
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo02" class="com.meimeixia.struts2.valuestack.ValueStackDemo02"></action>
    		<action name="valueStackDemo03" class="com.meimeixia.struts2.valuestack.ValueStackDemo03">
    			<result>/demo01/success.jsp</result>
    		</action>
    	</package>
    </struts>
    

    此时,WebContent/demo01目录下的success.jsp页面的内容不必修改。

最后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo03.action这样的URL地址进行访问,点击超链接即可看到值栈结构,如下图所示。
在这里插入图片描述

第二种方式:获取值栈对象,调用值栈对象本身的方法

首先,在com.meimeixia.struts2.valuestack包中创建一个Action类(ValueStackDemo04.java),在该类中,咱获取到值栈对象之后,调用值栈对象里面的push方法向值栈中存入一个User实例。

package com.meimeixia.struts2.valuestack;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * 操作ValueStack方式二:调用值栈中的一些方法来实现
 * @author liayun
 *
 */
public class ValueStackDemo04 extends ActionSupport {

	@Override
	public String execute() throws Exception {
		//向值栈中保存数据
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//我们一般会使用值栈中的push(Object obj)、set(String key, Object obj)这两个方法
		User user = new User("张小敬", "111");
		valueStack.push(user);//压栈,在栈的最顶端,即现在user在栈顶的位置
		
		return super.execute();
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo02" class="com.meimeixia.struts2.valuestack.ValueStackDemo02"></action>
    		<action name="valueStackDemo03" class="com.meimeixia.struts2.valuestack.ValueStackDemo03">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo04" class="com.meimeixia.struts2.valuestack.ValueStackDemo04">
    			<result>/demo01/success.jsp</result>
    		</action>
    	</package>
    </struts>
    

    此时,WebContent/demo01目录下的success.jsp页面的内容依旧不必修改。

最后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo04.action这样的URL地址进行访问,点击超链接即可看到值栈结构,如下图所示。
在这里插入图片描述
如果在ValueStackDemo04类中,获取到值栈对象之后,调用的是值栈对象里面的set方法向值栈中存入数据,那么值栈的内部结构会是什么样子呢?那就修改一下ValueStackDemo04类的代码,试着运行一下不就得了。

package com.meimeixia.struts2.valuestack;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * 操作ValueStack方式二:调用值栈中的一些方法来实现
 * @author liayun
 *
 */
public class ValueStackDemo04 extends ActionSupport {

	@Override
	public String execute() throws Exception {
		//向值栈中保存数据
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//我们一般会使用值栈中的push(Object obj)、set(String key, Object obj)这两个方法
		User user = new User("张小敬", "111");
		valueStack.push(user);//压栈,在栈的最顶端,即现在user在栈顶的位置
		
		valueStack.set("name", "李必");//压栈,在栈的最顶端,它是创建了一个Map集合,将Map集合压入到栈中
		
		return super.execute();
	}

}

此时,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo04.action这样的URL地址进行访问,点击超链接即可看到值栈结构,如下图所示。
在这里插入图片描述
向值栈中你都会存入Java对象了,难道会害怕存入一个小小的字符串吗?

向值栈中存入List集合

这里,我只讲解使用第一种方式向值栈中存入List集合。首先,在com.meimeixia.struts2.valuestack包中创建一个Action类(ListValueStack.java)。

package com.meimeixia.struts2.valuestack;

import java.util.ArrayList;
import java.util.List;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionSupport;

public class ListValueStack extends ActionSupport {
	
	// 1.声明list集合的变量
	private List<User> list = new ArrayList<User>();
	
	// 2.生成get方法
	public List<User> getList() {
		return list;
	}
	
	@Override
	public String execute() throws Exception {
		// 3.向值栈的list里面设置值
		User u1 = new User();
		u1.setUsername("如花");
		u1.setPassword("123");
		
		User u2 = new User();
		u2.setUsername("小金");
		u2.setPassword("321");
		
		list.add(u1);
		list.add(u2);
		
		return SUCCESS;
	}
	
}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo02" class="com.meimeixia.struts2.valuestack.ValueStackDemo02"></action>
    		<action name="valueStackDemo03" class="com.meimeixia.struts2.valuestack.ValueStackDemo03">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo04" class="com.meimeixia.struts2.valuestack.ValueStackDemo04">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="listValueStack" class="com.meimeixia.struts2.valuestack.ListValueStack">
    			<result>/demo01/success.jsp</result>
    		</action>
    	</package>
    </struts>
    

    此时,WebContent/demo01目录下的success.jsp页面的内容依旧不必修改。

最后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/listValueStack.action这样的URL地址进行访问,点击超链接即可看到值栈结构,如下图所示。
在这里插入图片描述

获取值栈中的数据

如何获取值栈中的数据中呢?想要获取的话,你得使用Struts2标签和OGNL表达式。另外,你还得知道很重要的一点:

  • 获取root中的数据时,不需要加#号;
  • 获取context中的数据时,需要加#号。

获取root中的数据

获取Java对象

如果我们使用上面讲述的第一种方式(即在Action成员变量位置定义变量,然后提供这个变量的get方法的这种方式)向值栈中存入一个User实例,就像ValueStackDemo03类一样,那么该如何从值栈中获取User实例中的数据呢?

package com.meimeixia.struts2.valuestack;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 操作ValueStack方式一:利用Action本身在值栈中的特性来实现的
 * @author liayun
 *
 */
public class ValueStackDemo03 extends ActionSupport {
	
	private User user;
	
	public User getUser() {
		return user;
	}

	@Override
	public String execute() throws Exception {
		//向ValueStack中存值
		user = new User("张敏", "123");
		return SUCCESS;
	}

}

此时,我们可以在success.jsp页面中通过Struts2标签和OGNL表达式获取值栈里面的User实例中的数据。
在这里插入图片描述
如果我们使用的是上面讲述的第二种方式(即获取值栈对象,调用值栈对象本身的方法这种方式)向值栈中存入一个User实例,就像ValueStackDemo04类一样,那么又该如何从值栈中获取User实例中的数据呢?

package com.meimeixia.struts2.valuestack;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * 操作ValueStack方式二:调用值栈中的一些方法来实现
 * @author liayun
 *
 */
public class ValueStackDemo04 extends ActionSupport {

	@Override
	public String execute() throws Exception {
		//向值栈中保存数据
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//我们一般会使用值栈中的push(Object obj)、set(String key, Object obj)这两个方法
		User user = new User("张小敬", "111");
		valueStack.push(user);//压栈,在栈的最顶端,即现在user在栈顶的位置
		
		valueStack.set("name", "李必");//压栈,在栈的最顶端,它是创建了一个Map集合,将Map集合压入到栈中
		
		return super.execute();
	}

}

此时,我们也可以在success.jsp页面中通过Struts2标签和OGNL表达式获取值栈里面的User实例中的数据。
在这里插入图片描述

获取List集合

首先,使用上面讲述的第二种方式(即获取值栈对象,调用值栈对象本身的方法这种方式)向值栈中存入List集合。于是,咱要在com.meimeixia.struts2.valuestack包中创建一个Action类(ValueStackDemo05.java)。

package com.meimeixia.struts2.valuestack;

import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.ServletActionContext;

import com.meimeixia.struts2.domain.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

/**
 * 获取值栈中的数据
 * @author liayun
 *
 */
public class ValueStackDemo05 extends ActionSupport {

	@Override
	public String execute() throws Exception {
		//向值栈中保存一个对象,然后在页面中获取出来
		User user = new User("liayun", "123");
		ActionContext.getContext().getValueStack().push(user);
		
		//向值栈中保存一个集合,而且集合里面我们还放对象
		List<User> list = new ArrayList<User>();
		list.add(new User("aaa", "111"));
		list.add(new User("bbb", "222"));
		list.add(new User("ccc", "333"));
		ActionContext.getContext().getValueStack().set("list", list);
		
		return super.execute();
	}

}

然后,在struts.xml核心配置文件中配置好该Action类,这里我们采用Struts2中分模块开发的配置。

  • struts.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<!-- 配置Struts2的常量 -->
    	<constant name="struts.action.extension" value="action" />
    	
    	<!-- 开启静态方法访问 -->
    	<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    	
    	<include file="com/meimeixia/struts2/valuestack/struts_demo01.xml"></include>
    </struts>
    
  • struts_demo01.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
    	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    	"http://struts.apache.org/dtds/struts-2.3.dtd">
    
    <struts>
    	<package name="demo01" extends="struts-default" namespace="/">
    		<action name="valueStackDemo01" class="com.meimeixia.struts2.valuestack.ValueStackDemo01">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo02" class="com.meimeixia.struts2.valuestack.ValueStackDemo02"></action>
    		<action name="valueStackDemo03" class="com.meimeixia.struts2.valuestack.ValueStackDemo03">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo04" class="com.meimeixia.struts2.valuestack.ValueStackDemo04">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="listValueStack" class="com.meimeixia.struts2.valuestack.ListValueStack">
    			<result>/demo01/success.jsp</result>
    		</action>
    		<action name="valueStackDemo05" class="com.meimeixia.struts2.valuestack.ValueStackDemo05">
    			<result>/demo01/success2.jsp</result>
    		</action>
    	</package>
    </struts>
    

紧接着,在WebContent/demo01目录下新建一个success2.jsp页面,在该页面中通过Struts2标签和OGNL表达式获取值栈里面List集合中的数据。
在这里插入图片描述
其实上面这种获取值栈里面List集合中的数据很傻逼,要是List集合中有成百上千的对象,那尼玛得写到什么时候,这个时候,我们很容易就想到了集合的遍历。恰好,在Struts2标签里面有一个可以进行遍历操作的标签,类似于JSTL标签库中的foreach标签,咱下面就用用。
在这里插入图片描述
我们现在操作的数据都是在值栈的root里面,如果在使用Struts2中的iterator标签时,在标签里面写了var,那么它就会把List集合中每次遍历出来的User对象放到context里面。而我们说了好几遍了,获取context里面的值,写OGNL表达式时,要求在OGNL表达式之前添加#号,所以,你可以写成下面这样。
在这里插入图片描述

获取context中的数据

我已说过了,操作值栈,通常指的就是操作ValueStack中的root区域。但如果现在向context区域中存入一些数据呢?就像下面这样,从ValueStackDemo05类的代码中可以看出,向context区域中存入数据,其实就相当于往那几个域里面存储数据。
在这里插入图片描述
由于我们在struts.xml核心配置文件中已经配置好了该Action类,所以只须修改一下success2.jsp页面,在该页面中通过Struts2标签和OGNL表达式获取context区域中的数据。
在这里插入图片描述
接着,你可以在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo05.action?id=123这样的URL地址进行访问,此时,你将会看到下面这样的结果。
在这里插入图片描述

EL表达式为什么能获取值栈数据?

首先,我们将success2.jsp页面修改成下面这样:
在这里插入图片描述
然后,在浏览器地址栏中输入http://localhost:8080/struts2_demo03/valueStackDemo05.action?id=123这样的URL地址进行访问,可以发现将值栈中存入的User实例的username属性值取出来了。
在这里插入图片描述
为啥会这样呢?EL表达式都能获取值栈中的数据?这里面的底层原因我们肯定得知道,归根结底就是要阅读源码了。通过查看StrutsRequestWrapper类的源码得知,Struts2框架的底层对request对象的getAttribute()方法进行了增强。
在这里插入图片描述
getAttribute()是用于获取域对象里面的值的方法,它的一个大致上的执行流程是:

  1. 首先到request域里面找是否有值,如果request域里面有值,直接返回;
  2. 如果域对象里面没有值,则得到值栈对象,从值栈对象里面把值获取到,最后放到域对象里面去。

OGNL中特殊字符的使用

#号的使用

获取context中的数据

我想我都不知道说过多少遍了,可以使用#号获取context中的数据,就像下面这样。
在这里插入图片描述

使用#号构建一个Map集合

我们使用#号还能构建一个Map集合呢!如果不使用#号的话,咱也能构建一个List集合,先说构建一个List集合这件事。还记得我前面提到过Struts2中的iterator标签吗?等一下,我们就使用这个标签来遍历List集合和Map集合,这个标签中有一个value属性,它代表的就是要遍历的东西,比如说这里面我们要遍历一个List集合。
在这里插入图片描述
那怎样才能构建一个List集合呢?只要你把value属性的值设置成{‘aa’, ‘bb’, ‘cc’},那么OGNL表达式就把{‘aa’, ‘bb’, ‘cc’}当成一个List集合来使用。
在这里插入图片描述
从上图可知,使用Struts2中的iterator标签时,如果在标签里面写了var,那么它就会把List集合中每次遍历出来的东西放到context里面,这样,我们在获取context里面的东东,写OGNL表达式时,要在OGNL表达式之前添加#号。
接下来,我们就使用一下#号,构建一个Map集合玩玩。如果你把value属性的值设置成#{‘aa’:‘11’, ‘bb’:‘22’, ‘cc’:‘33’},那么OGNL表达式就会把它当成Map集合来使用。
在这里插入图片描述
这里,我再引申一个知识点,之前我们在页面中是这样来写单选框的。
在这里插入图片描述
现在,咱就可以使用Struts2中的radio标签来写了。
在这里插入图片描述
如果在页面中是像下面这样来写单选框的,那么使用Struts2中的radio标签又该怎样写呢?
在这里插入图片描述
使用Struts2中的radio标签的话,可以这样写。
在这里插入图片描述

%号的使用

强制解析OGNL

%要和Struts2的表单标签一起使用,至于Struts2的表单标签,后面我会介绍到。如果使用Struts2的表单标签,并使用OGNL表达式来显示值,应该是下面这样。
在这里插入图片描述
此时,使用浏览器访问以上页面,可看到如下结果:
在这里插入图片描述
发现OGNL表达式作为字符串显示出来了,而没有作为OGNL表达式来执行。要解决这个问题,就要使用%号来强制解析OGNL表达式了(即使用%号让表单标签里面的值作为OGNL表达式执行)。
在这里插入图片描述
这时,才可看到正确的值。

$号的使用

我们还可在配置文件中使用OGNL表达式,不管这个配置文件是properties格式的文件,还是xml格式的文件。

在properties格式的属性文件中使用OGNL表达式

我们有可能在国际化的地方使用到properties格式的属性文件,那么在这个时候,就能在属性文件中使用到OGNL表达式了。
在这里插入图片描述

在xml格式的配置文件中使用OGNL表达式

我们还有可能在Struts2的配置文件中使用到OGNL表达式,而且主要是在做文件下载时会用到。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值