复习_Struts2_OGNL表达式、值栈

1.OGNL的用法详解

Ognl表达式一般是结合struts2框架的标签来使用的,最重要的作用,就是获取值栈中存储的数据。

在下面的示例,咱们将对ognl的各种用法进行测试,分析OGNL是如何使用的。

  1. 1.Java对象的直接访问
    例如:’itcast’
  2. 实例方法调用
    语法:object.doSomething(),object是对象实例
    例如:’itcast’.toUpperCase()
  3. 静态方法调用(类的静态方法或静态变量的访问)–不常用
    语法:@[类全名(包括包路径)]@[方法名|值名]
    例如:@java.lang.Math@max(10,20)
    注意:必须通过配置struts2的常量来开启静态方法调用功能:
    struts.ognl.allowStaticMethodAccess=true
  4. 赋值操作或表达式串联。–可以运算
    如计算1+2或者2*2
  5. 对集合对象的操作
    <s:property value="#{‘上海’:‘浦东’,‘北京’:‘丰台’}"/>
  6. Ognl对值栈的操作

示例:
搭建struts2的环境:导入jar、struts.xml、web.xml,引入struts标签库,编写表达式代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<!-- s:property标签中的value可以用来解析ognl表达式获取各类数据 -->

<!-- ognl表达式的用法 -->

<!-- s:property标签中的value 可以解析ognl表达式 -->
<s:property value="'itcast'"/><br>
<!-- 可以操作对象的实例方法 -->
<s:property value="'itcast'.toUpperCase()"/><br>
<!-- 可以操作静态方法 -->
<s:property value="@java.lang.Math@max(1,20)"/><br>
<!-- 操作集合 -->
<s:property value="{'上海','北京'}"/><br>
<!-- 操作map集合 -->
<s:property value="#{'上海':'浦东','北京':'丰台' }"/><br>
<!-- 操作表达式 -->
<s:property value="10%3"/><br>
</body>
</html>

应用ognl 表达式调用对象方法或者静态方法,需要结合struts2的\<s:property > 标签,其中提供的value属性支持ognl。

2.值栈概述

2.1 值栈(ValueStack)是什么

值栈(ValueStack),是Struts2的数据中转站(类似于超市)

他自动保存了当前Action对象和其他相关对象(包括常用的Web对象的引用,如request、application、session等),也可以手动保存自己的数据对象,同时也可以随时随地将对象从值栈取出或操作(通过OGNL表达式)

最核心的作用:存值

值栈(ValueStack)的接口是ValueStack类,实现类是OgnlValueStack类,该对象是Struts2利用OGNL的基础。只要我们用到ognl,那么就需要依赖值栈。

在这里插入图片描述
Struts2框架将ValueStack对象保存在名为“struts.valueStack”的请求(request)属性中,即值栈是request中的一个对象,一个请求对应一个Action实例和一个值栈对象。

值栈的获取方式:

  • request.getAttribute(“struts.valueStack”)
  • ActionContext.getContext().getValueStack()

该两种方式获取的值栈其实是同一个。


2.2 值栈的数据存储结构的分析

【示例1】
目标:值栈对象的获取,并证明两种方式获取的对象是同一个对象:
1)request.getAttribute(“struts.valueStack”)
2)ActionContext.getContext().getValueStack()

步骤一:编写action
创建包和action
public class DemoAction extends ActionSupport{
	@Override
	public String demo(){
		//获取值栈的第一种方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		//获取值栈的第二种方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack1);
		System.out.println(valueStack2);
		
		return SUCCESS; 
	}
}
<struts>
	<!-- 配置开发者模式 -->
	<constant name="struts.devMode" value="true"></constant>
	<package name="p1" extends="struts-default">
	
		<action name="demo" class="cn.itcast.valueStack.DemoAction" method="demo">
			<result name="success">/demo.jsp</result>
		</action>
	</package>
</struts>

在这里插入图片描述

【值栈小结】:
值栈包括两部分:root栈和map栈
1、root栈:继承了List接口,又称之为对象栈
2、map栈:实现了Map接口,又可以称之为上下文栈(context)
在这里插入图片描述


3.值栈的操作(重点)

3.1 Root栈的存值和取值

Root栈的存值:
它按照先进后出的原则存储数据,即先进入的数据被压入栈底,最后进入的数据在栈顶,需要读取数据的时候,从栈顶开始弹出数据(即最后一个数据被第一个读出来)。
栈也被成为先进后出表,可进行插入和删除操作,插入称之为进栈(压栈)(push),删除称之为退栈(pop)

【示例】
值栈数据的保存:向root栈存入数据,响应页面根据不同的方式(通过ognl表达式)分别将值获取出来。

1.Push方式压入栈顶
push方法:将数据压入root栈的栈顶。

public class DemoAction extends ActionSupport{
	@Override
	public String demo(){
		//获取值栈的第一种方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		//获取值栈的第二种方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack1);
		System.out.println(valueStack2);
		
		//将数据存入root栈,以push方法压入栈顶
		valueStack2.push("zhangsan");
		//继续将一个数据压入栈顶
		valueStack2.push("lisi");
		
		return SUCCESS;
	}
}

Root栈分析:
在这里插入图片描述
步骤二:设置响应页面利用ognl表达式从值栈取值
导入struts标签:

<%@taglib uri="/struts-tags" prefix="s" %>
demo.jsp页面内容:
<!-- 
		通过s:property标签获取数据 
		value:是一个ognl表达式,通过valu值可以到值栈中去查询对应的数据
		[0].top:表示获取root栈栈顶的数据
	-->
	获取栈顶数据1:<s:property value="[0].top"/><br>
	获取栈顶数据2:<s:property/><br>
	<s:debug></s:debug>
	执行成功!

步骤三:测试
在这里插入图片描述

2.Set方式压入栈顶
set方法:
将数据压入栈顶:会先创建一个map集合,让后将数据存入到集合中,最后将map集合压入栈顶。

步骤一:修改DemoAction :
public class DemoAction extends ActionSupport{
	
	public String demo(){
		//第一种获取值栈的方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		System.out.println(valueStack1);
		//第二种获取值栈的方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack2);
		
		//将数据存入root栈栈顶
		valueStack2.push("zhangsan");
		valueStack2.push("lisi");
		
		//set:创建一个map集合,将数据存入该集合中,最终将map集合压入root栈栈顶
		valueStack2.set("name","wangwu");
		
		return SUCCESS;//相当于"none",表示不响应任何结果集视图
	}
	
}
<%--
		如果栈顶是map集合:通过集合的key可以直接获取value值
		通过name到值栈中从上往下进行查找,找到的第一个进行返回
	 --%>
	 获取栈顶map中的数据:<s:property value="name"/><br>

3.通过action的成员变量将数据存入值栈
页面的<s :debug>标签会通过getName()方法从值栈中获取数据并显示在root栈中
步骤一:修改ValueStackAction
在action中添加成员变量,并设置成员变量的的getter和setter方法


3.2 Map栈的存值和取值

1.通过put方法将数据存入map栈

put方法:
将数据存入map栈,map栈是一个map,因此需要通过key来获取对应的value.
如果需要直接从map栈获取数据,建议在ognl表达式中添加一个#,表示直接从map栈查找,而不是从root栈栈顶开始从上往下查找。

如:<s:property value="#name"/>

修改DemoAction

public class DemoAction extends ActionSupport{
	private String name = "maliu";
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String demo(){
		//第一种获取值栈的方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		System.out.println(valueStack1);
		//第二种获取值栈的方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack2);
		
		//将数据存入root栈栈顶
		valueStack2.push("zhangsan");
		valueStack2.push("lisi");
		
		//set:创建一个map集合,将数据存入该集合中,最终将map集合压入root栈栈顶
		valueStack2.set("name","wangwu");

		//将数据存入map栈
		//由于map栈本质就是一个集合,因此存储方式和map一样
		ActionContext.getContext().put("name", "tianqi");
		return SUCCESS;//相当于"none",表示不响应任何结果集视图
	}
}

页面获取map栈数据

<%--
		从map栈获取数据:在ognl表达式中使用#表示直接从map栈查找数据
	 --%>
	 
	 获取map栈中的数据:<s:property value="#name"/>

3.3 值栈的存取小结

值栈的主要作用就是数据的保存和获取
使用值栈的时候要思考:用什么代码存进去,然后再用什么代码取出来

1.如何向值栈保存数据

  • ValueStack.push(obj) :保存数据到Root栈顶-压入栈顶
  • ValueStack.set(key,value):将数据保存到Root栈顶(数据被封装到map中,然后将map压入栈顶)–有key (不推荐)
  • 提供Action成员变量,提供setter和getter方法(Action就在root栈中,Action属性可以被搜索)
  • ActionContext.getContext().put(key,value) :保存数据到Map栈中—有key

2.如何向值栈保存数据

  • JSP页面获取

    • 通过 [index].top 指定访问root栈某个对象 ,例如<s:property value=”[0].top”/> ,获取栈顶对象的简写方式:<s:property/>
    • <s :property value= “name”/> 先搜索root栈的key,再搜索map的key ,将查找到的第一个数据进行返回 (不推荐)
    • <s:property value=”#name” /> 搜索map的key ,获取map栈中的数据
  • Action代码获取
    ValueStack.findValue(ognl表达式) ; 获取值栈数据

//代码怎么获取值栈数据(页面的ognl表达式获取数据的底层代码)
String str1=(String)ActionContext.getContext().getValueStack().findValue("username");
System.out.println(str1);
String str2=(String)ActionContext.getContext().getValueStack().findValue("#username");
System.out.println(str2);

值栈的主要目的:就是存进去,取出来。


3.4 值栈的创建过程源代码分析(了解)

值栈(和保存对象)的创建过程详解:

1.访问action的时候,走过滤器,StrutsPrepareAndExecuteFilter中创建了值栈(ValueStack<–OgnlValueStack),并将常用web对象保存到OgnlContext的Map中。
在这里插入图片描述
注意:此时root栈为空。

2.当访问Action的时候,过滤器会通过ActionProxy代理对象来执行。通过ActionProxyFactory的createActionProxy方法来得到代理对象。在获取代理对象的时候,需要创建一个ActionInvocation接口的默认实现类DefaultActionInvocation的对象,该默认对象在创建的时候会自动调用init方法来初始化,在初始化方法中会自动调用stack.push(action)方法,将当前Action对象,压入Root栈的顶部。
在这里插入图片描述
注意:再通过debug调试发现,默认情况下ValueStack的root栈存在2个对象 ,ValueStackAction(Action在栈顶)和DefaultTextProvider(用来初始化资源信息)

原理目的:

  • 知道值栈的初始化时机,访问action的时候,才创建(这个过程中会创建actionProxy对象,并且同时创建值栈,并且,将一些对象放入值栈。)
  • 值栈初始化之后,里面主要默认有:root栈(action类对象、text。。、model(user)),map栈(servlet相关对象、action对象一个引用)
  • 要了解:哪些对象在栈顶!因为后面我们从值栈取值,都从栈顶往下取。所以,一定要知道哪个对象在栈上面。

3.5 值栈的生命周期

【回顾分析】:

  • 贯穿整个Action的生命周期,每个Action类的对象实例都拥有一个ValueStack对象。
  • Struts2框架将ValueStack对象保存在名为“struts.valueStack”的请求(request)属性中,即值栈是request中的一个对象,一个请求对应一个Action实例和一个值栈对象。

值栈能够线程安全的为每个请求提供公共的数据存取服务。当有请求到达的时候,Struts2会为每个请求创建一个新的值栈,也就是说,值栈和请求是一一对应的,不同的请求,值栈也不一样,而值栈封装了一次请求所有需要操作的相关的数据。
因此:值栈的生命周期,就是request生命周期。

问题:如果结果集使用的是重定向,值栈中的数据存在吗?
答案:值栈存在于request中,request在一次请求结束后生命周期也随之结束,因此值栈的生命周期也就结束了,所以重定向之后的值栈是第二次请求发送时创建的一个新的值栈,找不到之前的数据了。


4.Struts2使用表达式语言来操作值栈

4.1 Struts2支持的表达式语言

Struts 2支持以下几种表达式语言:

  • OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言;
  • EL(Expression Language),可以方便的操作JSP页面四个域范围数据 (page、 request、 session、 application );
  • JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言;
  • Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python、Ruby和Smarttalk等)的一些起特性;
  • Velocity,严格来说不是表达式语言,它是一种基于Java的模板匹配引擎,具说其性能要比JSP好。

Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言更简单、强大,最重要的是可以直接操作值栈。


4.2 OGNL对Struts2的值栈的操作方式

Struts2 存取数据,通过值栈完成的, Ognl是访问值栈数据的表示式语言
在Struts2中,使用OGNL表达式获取数据有三种用法 :

  • # 号用法:获取map栈数据
  • % 号用法:强制解析或者不解析
  • $ 号用法:xml中获取值栈数据

1.#号的使用
在使用#获取map栈数据之前,我们对map栈的结构可以在进行一次深入的理解,map栈除了我们可以手动存储数据之外,还保存了其他的一些数据,主要如下:

Map的key(类型是String)Map的Value (类型是Object)说明信息
applicationJava.util.Map<String,Object>封装的应用域中的所有数据
sessionJava.util.Map<String,Object>封装的会话域中的所有数据
requestJava.util.Map<String,Object>封装的请求域中的所有数据
parametersJava.util.Map<String,String[]>封装的是请求参数
attrJava.util.Map<String,Object>封装的是四大域的组合数据,从最小的域开始搜索

那么如何来获取map栈中的各大域的数据呢?

用法一: 访问Map栈中常用对象,包括web对象, 添加#进行访问
在这里插入图片描述
提示:#attr 按照 page — request — session — application的顺序依次进行搜索

编写action

public class Demo2Action extends ActionSupport{
	
	public String demo2(){
		//将数据存入request
		ServletActionContext.getRequest().setAttribute("name", "request_name");
		//将数据存入session
		ServletActionContext.getRequest().getSession().setAttribute("name", "session_name");
		//将数据存入application
		ServletActionContext.getServletContext().setAttribute("name", "application_name");
		return SUCCESS;
	}
}

Struts.xml

<action name="demo2" class="cn.itcast.expr.Demo2Action" method="demo2">
	<result name="success">/demo2.jsp</result>
</action>

创建页面

<body>
  内容:
  参数范围:<s:property value="#parameters.name"/>|${param.name }<br>
  request范围:<s:property value="#request.name"/>|${requestScope.name }<br>
  session范围:<s:property value="#session.name"/>|${sessionScope.name }<br>
  application范围:<s:property value="#application.name"/>|${applicationScope.name }<br>
  page范围:<s:property value="#attr.name"/>|${name }<br>
</body>

显示如下:
在这里插入图片描述
注意:attr依次从page --- request --- session --- application的顺序依次进行搜索。


2.%号的使用
%主要作用是控制解析或者不解析

Struts标签分为普通标签和表单标签两种:
普通标签是通过属性value的ognl表达式获取值栈数据的,比如:<s:property value=””>
表单标签是通过name属性的ognl表达式获取值栈数据的,value属性只能存入字符串,比如:<s:textfield value=“username”>

%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式,就比如:<s:textfield value=”%{username}”>。 也可在OGNL表达式中,添加%{‘ ’}或者’’,来让其变成普通字符串而不解析,比如:<s:property value=”%{‘username}’”>
简单的说:%表达式的作用是,可以让字符串变成支持ognl表达式的解析,也可以让解析的表达式变成字符串,

public class Demo3Action extends ActionSupport{
	
	public String demo3(){
		ActionContext.getContext().put("name", "zhangsan");
		return SUCCESS;
	}
}
<action name="demo3" class="cn.itcast.expr.Demo3Action" method="demo3">
	<result name="success">/demo3.jsp</result>
</action>

demo3.jsp页面

创建一个demo3.jsp页面:
<body>
	<!-- 获取map栈数据 -->
	<s:property value="#name"/><br>
	<!-- 
		强制解析
		s:textfield:该标签的value属性并不支持ognl表达式,为了能够获取数据就需要强制解析成一个ognl表达式
	 -->
	<s:textfield value="%{#name}"></s:textfield><br>
	<!-- 
		不解析
	 -->
	 <s:property value="%{'name'}"/><br>
</body>

在这里插入图片描述

3.$号的使用
$主要作用是在相关配置文件中引入OGNL表达式,让其在配置文件中也能解析OGNL表达式。(换句话说:$用于在配置文件中获取值栈的值用的。)

需求:通过结果集类型重定向到另外一个页面的时候,在第二次发送的请求地址后面拼接一个从值栈中获取到的数据。

步骤一:编写action

public class Demo4Action extends ActionSupport{
	
	public String demo4() throws UnsupportedEncodingException{
		ActionContext.getContext().put("name", "lisi");
		return SUCCESS;
	}

}

步骤二:struts.xml:

<action name="demo4" class="cn.itcast.expr.Demo4Action" method="demo4">
	<result name="success" type="redirect">/demo4.jsp?name=${#name}</result>
</action>

jsp页面

新建一个demo4.jsp页面
<body>
	<h1>执行成功!</h1>
</body>

在这里插入图片描述

4.3 EL表达式获取值栈的数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值