struts2中的ognl

1、ValueStack与<s:debug>标签
Struts 2框架使用OGNL作为默认的表达式语言,先用<s:property> 标签体验一下ognl表达式和调动学习热情,例如,显示Action中的一个属性和显示一个请求参数。
OGNL表达式就是针对一个称为OGNL Context的Map对象和一个称之为OGNL根对象进行操作的语言,OGNL表达式可以寻址Context内部的对象和直接调用根对象的属性或方法。
Struts2中设置的OGNL 根对象为CompoundRoot, OGNL Context的Map对象和CompoundRoot 封装在一个ValueStack对象中。由于OGNL Context是一个Map对象,不算什么新知识,不用专门学习了,所以,学习OGNL的重要第一步就是要先了解ValueStack和CompoundRoot 。ValueStack内部封装的CompoundRoot是一个继承了ArrayList实现的堆栈,ValueStack内部还保存了ValueStack Context的引用。(见下页的关系图)
<s:debug>标签可以在jsp页面中查看ValueStack和其关联的Context对象中的信息。
如果不经过struts2的过滤器拦截就直接访问jsp页面,那么其中使用的<s:debug>,<s:text>,<s:property value="%{getText()}">等struts2标签都会报告TagUtil.getStack方法抛出的异常。
获取ValueStack对象的代码如下:
ValueStack stack = ActionContext.getContext().getValueStack();
ValueStack提供了如下一些方法管理其内部的堆栈:
push和pop方法分别对堆栈进行压栈和弹栈。
set方法用于在栈顶的Map对象中设置一个条目,如果栈顶不是一个Map对象,则创建一个Map对象并将其压入栈顶。实验结果:set方法好像不能设置栈顶不同JavaBean对象的属性。

Struts2框架为OGNL Context内部初始填充的几个重要的key:
parameters一个map对象
request 一个map对象
session 一个map对象
application 一个map对象
attr 一个map对象

2、OGNL基础与ValueStack的有关方法:
OGNL(对象图导航语言,Object Graph Navigation Language)是一种用于从ValueStack对象与其关联的Context上下文中获取的值的表达式,最基本的语法如下:
1.可以用#key的形式访问OGNL Context对象中的各个key对应的对象,并可以采用点(.)操作符进行多级导航调用对象的属性和方法,例如,#application、#session.attr1、#key1.sayHello();对于map对象,map.attr不是map.getAttr()方法,而是表示map.get(“attr1”)。
2.如果要访问根对象的属性或方法,则可以省略#key,直接访问该对象的属性和方法。 struts2修改了OGNL表达式的默认属性访问器,它不是直接访问根对象ValueStack的属性或方法,而是在ValueStack内部的堆栈中所有对象上逐一查找该属性或方法,搜索顺序是从栈顶对象开始寻找,依次往下,直到找到为止,例如,sayHello()表示调用堆栈中某个对象的sayHello()方法。
3.特例:如果引用名前面没有#,且valueStack中存储的各个对象没有该属性,则把该名称当作Context对象中的某个key来检索对应的对象,但这种方式不支持点(.)操作符。
ValueStack提供了如下一些方法管理其内部的堆栈和关联的Context:
setValue为ognl表达式寻址到的对象设置属性值。
findValue方法使用OGNL表达式获取结果。
findString方法对findValue方法获取的结果调用转换器转成字符串,如果该对象的类型没有相关转换器,则调用toString方法,并返回结果字符串。一个特殊之处:如果不用#前缀访问ValueStack Context中的对象,则该对象必须是String类型。

<s:property>标签用于输出某个OGNL表达式的值,可以认为其内部使用的是ValueStack对象的findString方法。
如果没有设置value属性,则输出ValueStack栈顶的对象。
特例:如果采用不加#前缀的方式输出Context中的某个对象,这个对象必须是string类型。
<s:push>标签用于将OGNL表达式的值压入栈顶。
看<s:push>标签的帮助,可以知道有一个为top的特殊OGNL表达式,表示栈顶的对象。
<s:bean>标签用于实例化一个JavaBean对象,并将其压入栈顶。
如果设置了var属性,还会将实例化的对象存储进ValueStack关联的Context中。
实验步骤:
查看<s:debug>标签的执行结果。
用<s:push>标签把Context中的一个对象压入栈顶,再查看<s:debug>标签的执行结果,注意<s:debug>标签要位于<s:push>标签对之间。
用<s:bean>标签用于实例化一个java.util.Date实例对象,查看<s:debug>标签的执行结果,注意<s:debug>标签也要位于<s:push>标签对之间;接着设置其var属性,再查看<s:debug>标签的执行结果。

<s:set>标签用于将某个值存入指定范围域中,通常用于将一个复杂的ognl表达式用一个简单的变量来进行引用。
scope属性:指定变量被放置的范围,该属性可以接受application、session、request、 page或action(context)。该属性的默认值为action,文档说即表示同时存储进request作用域和OGNL Context中,但实验结果是只存储进了OGNL Context中。
value属性:赋给变量的ognl表达式结果.如果没有设置该属性,则将ValueStack栈顶的值赋给变量。
<s:if/elseif/else>等标签用于判断test属性中指定的ognl表达式的结果是否为true,为真则执行标签体重的内容。
<s:iterator>标签用于迭代一个OGNL集合,并逐一将迭代出来的元素压入栈顶和弹栈。
status属性:创建代表当前迭代状态的IteratorStatus对象,并指定将其存储进ValueStack Context中时的key。
输出迭代后的ValueStack栈顶对象的属性并利用迭代状态的示例代码如下:
<s:iterator value="#request" status="status">
<tr class='<s:property value="#status.odd ? 'odd':'even'"/>' >
<td><s:property value="key"/>::::<s:property value="value"/></td>
</tr>

3、OGNL的语法细节:
参看ognl的参考手册
类似EL表达式的JavaBean属性访问和索引访问,例如,可以用”#parameter.id[0]”或”#parameter[‘id’][0]”访问名称为id的请求参数。
问题:ValueStack Context有一个名称为com.opensymphony.xwork2.ActionContext.locale的key,使用一个怎样的OGNL表达式可引用这个key对应的值对象呢?答:可以先从request中得到valueStack,再从ValueStack获得context,再从context中用[]的方式获取。
支持类静态方法调用和属性访问,表达式的格式为@[类全名(包括包路径)]@[方法名 |  值名],例如:@java.lang.String@format(‘foo %s’, ‘bar’)或@cn.itcast.Constant@APP_NAME;
session.attribute[“foo”]等效于session.getAttribute(“foo”)方法。
在OGNL中可以写很大的整数,例如,<s:property value="%{1111111111111111111111H.bitLength()}"/>,而在java中则不能直接写1111111111111111111111这么大的整数。
对当前值可以进一步操作,<s:property value=“110H.intValue().(#this<112?#this*2:#this/2)”/>,其中.(#this …..)部分相当于定义了一个匿名方法,并调用这个匿名方法,方法的代码就是()里面的内容。
Struts2扩展的特殊功能
[n]表示从原来堆栈中截取一个子堆栈并对这个子堆栈进行操作,子堆栈为原始堆栈的栈顶开始的索引号为n的元素一直到栈底,例如,[1].age表示从原始堆栈中的索引号为1的对象(即第二个对象)开始查找age属性,以找到的第一个为准。
top表示ValueStack栈顶的对象,[0].top和top表示同一个对象。

{}用于创建List集合对象,其中的各个元素之间用逗号分隔。
<s:set value=“{1,3,5,7}” var=“list”/>
采用类似Java的语法创建数组
<s:set value=“new int[]{1,3,5,7}” var=“array”/>
<s:set value=“new int[4]” var=“array”/>
#{}用于创建Map集合对象,其中的各个元素之间用逗号分隔,元素的key和value之间采用冒号分隔。另外,还可以指定Map实例对象的类型。
<s:set value=“#{‘lhm’:96,’zxx’:93,’xpc’:97}”
<s:set value=“#@java.util.LinkedHashMap@{‘lhm’:96,’zxx’:93,’xpc’:97}”
in与not in操作符用于判断某个值是否位于某个集合中。
集合伪属性:size/isEmpty/iterator/keys/values/next/hasNext

投影就是拿着集合中的每个元素去进行运算,各个元素运算的结果组成一个新集合,新集合中的元素个数与原始集合中的元素个数相同。
<s:property value=“persons.{name}”/>
<s:property value=“{5,3,2}.{#this*2}”/>
过滤就是拿着集合中的每个元素去进行布尔运算,运算的结果为true,则将该元素保存到新集合中去。
?:获得所有符合逻辑的元素。
<s:property value=“{5,3,2}.{? #this }”/>
^:获得符合逻辑的第一个元素。
<s:property value=“{5,3,2}.{^ #this>3 }”/>
$:获得符合逻辑的最后一个元素。
<s:property value=“{5,3,2}.{$ #this>2 }”/>

转换成boolean类型
整数0转换为false
值为0的字符转化为false
Null对象转化为false
投影和选择操作符(e1.{e2} and e1.{?e2})前面的内容会被转换成集合
Map会被转化成其values属性返回的集合
数字会被转换成从0开始到比该数字小1的所有数字的集合。
单个对象被转换成仅仅只含有该对象的集合。

4、在配置文件中使用OGNL:
在struts2的各种配置文件中也可以使用OGNL,只需要将OGNL表达式套在${}中,即${ognl表达式}。
总结列表:对${ognl}的总结涉及一些大家暂时还没学习到的内容,大家可以在学完相关知识后再来看这个总结列表
在struts.xml文件的<result>元素中,经常需要使用${属性名}表达式访问action中的属性,表达式里的属性名对应action中的属性。如下:
<result type="redirect">view.jsp?id=${id}</result>
在校验配置文件的<message>元素中,可能需要使用${getText(‘key’)}表达式访问国际资源包的消息。
在国际化资源文件中,也可以使用${ognl表达式},例如,使用${getText(‘key’)}表达式来获取国际化消息填充另一个资源的占位符。
在jsp页面中可以使用${获取属性的ognl表达式},但不能在${}使用调用方法的ognl表达式,否则,会把它当作el表达式的自定义函数。

<s:url>和<s:a>标签的四个好处:
使用<s:url action=“” namespace=“” method=“”/>不用关心web应用程序的路径和Action映射的扩展名。<s:url>中没有指定namespace属性时,将根据浏览器当前所访问的url地址来推测包名,并与action属性指定的内容一起生成最终的url地址。
可以自动附加jsessionid参数进行url重写。
可以对参数信息进行url编码。 备注:jstl中的<c:url>标准标签也有<s:url>标签的后两个作用。
直接使用<s:url />标签可以获得当前地址,只有使用includeParams属性会带上原来的请求参数。大型网站的链接地址总是要带上userid之类的信息。
实验步骤:
启动一个新浏览器访问第一个Action,查看<s:url>生成的超链接地址,刷新的访问,再查看<s:url>生成的超链接地址,比较差异说明<s:url>标签的url重写功能。
在<s:url>的action属性值的路径后也可以直接加参数,但不能对其中的中文进行编码。使用<s:param>子标签设置一个或多个参数,可以完成中文参数的url编码。
再写一个<s:url>标签,其中不带任何属性,并在访问地址中增加一些参数,查看其生成的url地址;接着设置其includeParams属性,再查看结果。这里应该打开标签的帮助文档进行讲解。
为上面的<s:url>标签增加var属性,并用<s:property>标签输出var属性指定的变量。
问题:使用s:url>标签的value属性时,其中的值不会当作OGNL表达式处理,而是当作普通字符串处理,如果value的内容要根据表达式运算,如何做?
在value属性值中使用%{},struts2就会把其中内容当作ognl表达式处理。


测试代码:
<%@page import="com.opensymphony.xwork2.ActionContext"%>
<%@page import="java.util.List"%>
<%@page import="java.util.Arrays"%>
<%@page import="cn.itcast.struts2valuestack.domain.User"%>
<%@ 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>Ognl标签应用</title>
</head>
<bodys>
	<!--
		 value : ognl表达式
		 default : 默认值
		 escapeHtml : true 如果文本中有标签,那么转义
	 -->
	<s:property value="name" default="表达式没有找值" escapeHtml="true"/><br/>
	
	获取map中值:<s:property value="#name"/><br/>
	
	<!-- #context Ongl内置的命令 -->
	获取map中特殊key的值:<s:property value="#context.get('com.opensymphony.xwork2.ActionContext.locale')"/><br/>
	获取map中特殊key的值:<s:property value="#context['com.opensymphony.xwork2.ActionContext.locale']"/><br/>
	
	获取ValueStack栈:<s:property/><br/>
	
	<!-- 不加#在栈中查找属性,如果没有找到,它在下面Map中查找,并且强行转为String类型 -->
	获取map中的值:<s:property value="haha"/><br/>
	
	
	<!--
		value : ognl表达式
		通过ognl表达式获取值,并把值压入栈顶 ,标签结束弹栈
	 -->
	<s:push value="#currentDate">
		当前毫秒数:<s:property value="time"/><br/>
	</s:push>
	
	
	<!--
		var : 存储在map中变量的名字
		name : 需要创建对象的类全名 
	 -->
	<s:bean var="myDate" name="java.util.Date"></s:bean>
	
	
	<!--
		 var : 存储在作用域中变量的名字
		 value : ognl表达式
		 scope : application, session, request, page, or action(map),默认是action
	 -->
	<s:set var="varxxx" value="name" scope="action"/>
	
	<!--
		 把ognl强行当初一个字符串处理,%{'字符串'}
	 -->
	<s:set var="varyyy" value="%{'你好啊'}" scope="action"/>
	
	<!-- if elseif esle -->
	<!-- test:ognl表达式 -->
	<s:if test="1<0">
		1<0(if)
	</s:if>
	<s:elseif test="1!=1">
		1!=1(elseif)
	</s:elseif>
	<s:else>
		1==1(else)
	</s:else>
	
	
	<%
		User user1 = new User("张三","123456");
		User user2 = new User("李四","123456");
		User user3 = new User("王五","123456");
		User user4 = new User("赵六","123456");
		User user5 = new User("田七","123456");
		List<User> users = Arrays.asList(user1,user2,user3,user4,user5);
		ActionContext.getContext().put("users",users);
	%>
	
	<%-- <s:iterator >迭代器 --%>
	<h3>第一种迭代(可以使用)</h3>
	<table border="1">
		<tr>
			<th>用户名</th>
			<th>密码</th>
		</tr>
		<!--
			 var : 没迭代一次,每个对象名字
			 value : ognl表达式
		 -->
		<s:iterator var="item" value="#users">
			<tr>
				<td>
					<s:property value="#item.userName"/>
				</td>
				<td>
					<s:property value="#item.password"/>
				</td>
			</tr>
		</s:iterator>
	</table>
	
	<h3>第二种迭代(推荐使用)</h3>
	<table border="1">
		<tr>
			<th>序号</th>
			<th>用户名</th>
			<th>密码</th>
		</tr>
		<!--
			 var : 没迭代一次,每个对象名字
			 value : ognl表达式
			 status : 迭代器状态对象,比如:index,count....,对应org.apache.struts2.views.jsp.IteratorStatus类
			 "把当前迭代出来的对象,压入栈顶,迭代完后,弹栈"
		 -->
		<s:iterator value="#users" status="mystatus">
			<tr>
				<td>
					<s:property value="#mystatus.count"/>
				</td>
				<td>
					<s:property value="userName"/>
				</td>
				<td>
					<s:property value="password"/>
				</td>
			</tr>
		</s:iterator>
	</table>
	
	<%
		//request.setAttribute("userName","我是request中东东....");
		//System.out.println(request);
	%>
	
	<h3>第三种迭代(不推荐使用)</h3>
	<table border="1">
		<tr>
			<th>用户名</th>
			<th>密码</th>
		</tr>
		<!--
			 var : 每迭代一次,每个对象名字
			 value : ognl表达式
			 "把当前迭代出来的对象,压入栈顶,迭代完后,弹栈"
		 -->
		<s:iterator value="#users">
			<tr>
				<td>
					${userName}
				</td>
				<td>
					${password}
				</td>
			</tr>
		</s:iterator>
	</table>
	
	<h3>OGNL的语法细节</h3>
	
	<%--
		String.format("hello %s !","张三");
		 支持类静态方法调用和属性访问 ,需要加入配置
		 
		 <!-- ognl表达式执行静态方法调用 -->
		<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
	--%>
	<s:property value="@java.lang.String@format('hello %s','张三')"/><br/>
	
	<%--
		Struts2扩展的特殊功能
	 --%>
	 获取栈顶:<s:property/><br/>
	 获取栈顶:<s:property value="top"/><br/>
	[n(number)]:<s:property value="[1].top"/><br/>
	
	<h3>OGNL的语法细节——集合对象与操作</h3>
	
	<%--{}用于创建List集合对象,其中的各个元素之间用逗号分隔 --%>
	<s:set var="myList" value="{1,2,3,4,5,6,7,8}"/>
	<s:property value="#myList[0]"/><br/>
	
	<!-- 采用类似Java的语法创建数组 -->
	<s:set value="new int[]{1,3,5,7}" var="array1"/>
	<s:property value="#array1"/><br/>
	<s:set value="new int[4]" var="array2"/>
	<s:property value="#array2"/><br/>
	
	<%--
		#{}用于创建Map集合对象,其中的各个元素之间用逗号分隔,元素的key和value之间采用冒号分隔。
		另外,还可以指定Map实例对象的类型。
	--%>
	<s:set value="#{'key1':'value1','key2':123456}" var="myMap"/>
	<s:property value="#myMap"/><br/>
	 
	<!-- in与not in操作符用于判断某个值是否位于某个集合中。 -->
	in:<s:property value="5 in #myList"/><br/>
	not in:<s:property value="5 not in #myList"/><br/>
	
	<!-- 集合伪属性:size/isEmpty/iterator/keys/values/next/hasNext -->
	 集合伪属性:<s:property value="#myList.size"/><br/>
	 
	 
	
	<h3>OGNL的语法细节——集合的投影与过滤</h3>
	<%
		User user11 = new User("张三","123456");
		User user12 = new User("李四","123456");
		User user13 = new User("王五","123456");
		User user14 = new User("赵六","123456");
		User user15 = new User("田七","123456");
		List<User> myusers = Arrays.asList(user11,user12,user13,user14,user15);
		ActionContext.getContext().put("myusers",myusers);
	%>
	<%-- 投影就是拿着集合中的每个元素去进行运算,各个元素运算的结果组成一个新集合,新集合中的元素个数与原始集合中的元素个数相同。--%>
	<s:property value="#myusers.{userName}"/><br/>
	
	<%-- #this集合中的每一个元素 --%>
	<s:property value="{5,3,2}.{#this*2}"/><br/>
	
	<%--过滤就是拿着集合中的每个元素去进行布尔运算,运算的结果为true --%>
	
	<!-- ?:获得所有符合逻辑的元素 -->
	<s:property value="{5,3,2}.{? #this>0}"/><br/>
	
	<!-- ^:获得符合逻辑的第一个元素 -->
	<s:property value="{-1,5,3,2}.{^ #this>0}"/><br/>
	
	<!-- $:获得符合逻辑的第一个元素 -->
	<s:property value="{5,3,2}.{$ #this>0}"/><br/>
	
	<a href="testOgnl">在配置文件中使用OGNL</a>
	
	
	<%--<s:url>与<s:a>标签  --%>
	<!--
		action : struts2中action的名字
		namespace : struts2package的命名空间 
		
		url 它会自动加上appPath(<Context path="/xxx"/>)
	 -->
	<br/> 
	<a href="<s:url action="testOgnl" namespace="/xxx"></s:url>">
		testOgnl
	</a> 
	<br/>
	<%ActionContext.getContext().put("id","010");%>
	<%--
		把ognl当成一个字符串执行 %{'字符串'}
		把一个字符串当成ognl表达式执行 %{表达式}
		
		includeParams : 把"get"请求参数,加载url后面
		includeParams : 把"get"和"post"请求参数,加载url后面
	 --%>
	<s:a action="testOgnl?username=009&id=%{#id}" namespace="/xxx" includeParams="all">
		testOgnl
		<%--value : ognl表达式 ,如果使用<s:param>配置请求参数,名字不能一样--%>
		<s:param name="username">007</s:param>
		<%--value : ognl表达式 --%>
		<s:param name="username" value="%{'008'}"></s:param>
	</s:a>
	
	<s:debug/>
</body>
</html>

你可能感兴趣的:(Ognl)