Struts2的值栈

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表达式的特殊的符号用法

  1. #号的用法:
    获得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>
    
  2. %号的用法
    强制字符串解析成OGNL表达式:

    <!-- 在 request域中存入值,然后在文本框 <s:textfield>中显示存入的值 -->
    <s:textfield value="%{#request.msg}"/>
    
    <!-- {}中值用''引起来,此时不再是 ognl表达式,而是普通的字符串 -->
    <s:property value="%{'#request.msg'}"/>
    
  3. $号的用法
    在配置文件中可以使用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>
    
发布了25 篇原创文章 · 获赞 0 · 访问量 467

猜你喜欢

转载自blog.csdn.net/weixin_45990046/article/details/103690514