Struts2第二天,大家似乎没有从年假中苏醒过来,上课显得有些疲惫。不过还好,听课效果还不错。今日的主要内容是OGNL,Struts2使用OGNL访问contextMap和valueStack。老张很执着,他在研究Struts2时遇到的一些问题,一定要解决,并且要详细解决。通过他在讲课中,我们可以看出这一点。有开发经验的人都知道,我们时而被陌生技术的一个小细节搞的晕头转向,耗费了大半天的时间我们才发现这个细节性的错误。我们一般都是这么熬出来的,老张也不例外。
我们在听老师讲课时或在网上看视频时,都感觉学习起来十分容易。却很少有人能感受那些认真负责老师在课后付出的心血。老张为了准备一天的课程内容,使用了三天时间来备课并且课程内容他以前也讲过。这一点不仅体现在老张的身上,其他老师也是这样。让我们为那些认真负责的老师敬礼,并问候一声:“过年好,您辛苦了!”。
一、ActionContext
Action环境(com.opensymphony.xwork2.ActionContext),通过我们之前所学的内容。我们知道Context缓存中保存了XXX环境中所需要使用到的重要对象。Struts2中的ActionContext中就保存了Struts2中使用到的重点对象contextMap和valueStack。
老张搞了一个经典的关系图:
ActionContext ac = ActionContext.getContext();
ValueStack vs = ac.getValueStack();
Map<String, Object> conMap = ac.getContextMap();
每一个访问线程具有一个ActionContext对象,它是以单例模式存在的。ActionContext为我们提供了操作valueStack和contextMap的便捷方法。例我们的程序可以与ValueStack和ContextMap解耦,比如:ac.put(key, value);、ac.get(key);...。
通过ActionContext使得我们编写的Action不必实现任何接口和继承任何超类,但我们必须在类中使用ActionContext的实例。
二、Struts2中的OGNL
OGNL不是Struts2独创的,而是Struts2中使用OGNL做为操作contextMap和valueStack的方式。OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言(Expression Language,简称为EL),通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
OGNLl基本的语法:
-
可以用#key的形式访问OGNL Context对象中的各个key对应的对象,并可以采用点(.)操作符进行多级导航调用对象的属性和方法,例如,#application、#session.attr1、#key1.sayHello();对于map对象,map.attr不是map.getAttr()方法,而是表示map.get(“attr1”)。
-
如果要访问根对象的属性或方法,则可以省略#key,直接访问该对象的属性和方法。 struts2修改了OGNL表达式的默认属性访问器,它不是直接访问根对象ValueStack的属性或方法,而是在ValueStack内部的堆栈中所有对象上逐一查找该属性或方法,搜索顺序是从栈顶对象开始寻找,依次往下,直到找到为止,例如,sayHello()表示调用堆栈中某个对象的sayHello()方法。
-
特例:如果引用名前面没有#,且valueStack中存储的各个对象没有该属性,则把该名称当作Context对象中的某个key来检索对应的对象,但这种方式不支持点(.)操作符。
ValueStack提供了如下一些方法管理其内部的堆栈和关联的Context:
-
setValue为ognl表达式寻址到的对象设置属性值。
-
FindValue方法使用OGNL表达式获取结果。
-
findString方法对findValue方法获取的结果调用转换器转成字符串,如果该对象的类型没有相关转换器,则调用toString方法,并返回结果字符串。一个特殊之处:如果不用#前缀访问ValueStack Context中的对象,则该对象必须是String类型。
三、Struts2中的struts-tags与OGNL详细
要使用struts-tags标签,必须先引入struts-2.1.7.dtd文档类型定义文件。
-
<s:property>:用于输出某个OGNL表达式的值,可以认为其内部使用的是ValueStack对象的findString方法。如果没有设置value属性,则输出ValueStack栈顶的对象。
特例:如果采用不加#前缀的方式输出Context中的某个对象,这个对象必须是string类型。
如:获取contextMap中的request对象。
<s:property value="#request" />
|
-
<s:push>:用于将OGNL表达式的值压入栈顶,当遇到</s:push>标签时,从栈顶弹出。
如:向栈顶压入了一个字符串——“你好!”。
<s:push value="'你好!'">
<s:property value="top" />
</s:push>
|
注意压入的字符串必须被包含在''中,否则会被OGNL当做名称到根对象中查找相应的值。
通过查看<s:push>标签的帮助,可以知道有一个为top的特殊OGNL表达式,表示栈顶的对象。
如:
<s:push value="'你好!'"><s:property name="top"/></s:push>
|
-
<s:bean>:用于实例化一个JavaBean对象,并将其压入栈顶。如果设置了var属性,还会将实例化的对象存储进ValueStack关联的Context中。
如:实例化一个java.util.Date对象,然后将其放入ContextMap中。
<s:bean name="java.util.Date" var="MyBean">
<s:property value="#MyBean" />
</s:bean>
|
-
<s:set>:用于将某个值存入指定范围域中,通常用于将一个复杂的ognl表达式用一个简单的变量来进行引用。
如:向request域中添加一个名称为“TestSet”的属性。
<s:set scope="request" value="'日期'+#MyBean" name="TestSet">
<s:property value="#request.TestSet" />
</s:set>
|
scope属性:指定变量被放置的范围,该属性可以接受application、session、request、 page或action。该属性的默认值为action,文档说即表示同时存储进request作用域和OGNL Context中,但实验结果是只存储进了OGNL Context中。
value属性:赋给变量的ognl表达式结果.如果没有设置该属性,则将ValueStack栈顶的值赋给变量。
-
<s:if/elseif/else>:等标签用于判断test属性中指定的ognl表达式的结果是否为true,为真则执行标签体重的内容。
如:设置一个VAL,然后判断VAL的值,并显示相应的结果。
<s:set value="'1'" name="VAL"></s:set>
<s:if test="1 > 2">
<s:property value="#VAL+'>2'" />
</s:if>
<s:elseif test="1 < 2">
<s:property value="#VAL+'<2'" />
</s:elseif>
<s:else>
<s:property value="#VAL+'=2'" />
</s:else>
|
-
<s:iterator>:用于迭代一个OGNL集合,并逐一将迭代出来的元素压入栈顶和弹栈。
如:获取request中的所有属性,并且使表格奇数行颜色为#f3c3c3,偶数行颜色为#c3f3f3。
<table>
<tr>
<td>Key</td><td>Value</td>
</tr>
<s:iterator value="#request" status="status">
<tr bgcolor='<s:property value="#status.odd ? '#f3c3c3':'#c3f3f3'"/>' >
<td><s:property value="key"/></td>
<td><s:property value="value"/></td>
</tr>
</s:iterator>
</table>
|
status属性:创建代表当前迭代状态的IteratorStatus对象,并指定将其存储进ValueStack Context中时的key。它与官方给出的帮助文档的说明不同,官方存在错误!
-
<s:url>和<s:a>:
使用这两个标签可以带给我们极大的方便,使用他们的好处有:
使用<s:url namespace=“” action=“” method=“”/>不用关心web应用程序的路径和Action映射的扩展名。<s:url>中没有指定namespace属性时,将根据浏览器当前所访问的url地址来推测包名,并与action属性指定的内容一起生成最终的url地址。
可以自动附加jsessionid参数进行url重写。
可以对参数信息进行url编码。 (jstl中的<c:url>标准标签也有<s:url>标签的后两个作用。)
直接使用<s:url />标签可以获得当前地址,只有使用includeParams属性会带上原来的请求参数。大型网站的链接地址总是要带上userid之类的信息。
如:生成一个访问我们昨天编写的HelloWorld请求超连接。
<s:a namespace="/" action="helloWorld" method="helloWorld">HelloWorld</s:a>
|
四、OGNL中的语法细节
1.参看ognl的参考手册
类似EL表达式的JavaBean属性访问和索引访问,例如,可以用”#parameter.id[0]”或”#parameter[‘id’][0]”访问名称为id的请求参数。
支持类静态方法调用和属性访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],例如:@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 …..)部分相当于定义了一个匿名方法,并调用这个匿名方法,方法的代码就是()里面的内容。
2.Struts2扩展的特殊功能
[n]表示从原来堆栈中截取一个子堆栈并对这个子堆栈进行操作,子堆栈为原始堆栈的栈顶开始的索引号为n的元素一直到栈底,例如,[1].age表示从原始堆栈中的索引号为1的对象(即第二个对象)开始查找age属性,以找到的第一个为准。
top表示ValueStack栈顶的对象,[0].top和top表示同一个对象。
3.集合对象与操作
{}用于创建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操作符用于判断某个值是否位于某个集合中。
<s:set value="new int[]{1,3,5,7}" var="array"/>
<s:if test="1 in #array">
<s:property value="#array"/>
</s:if>
|
集合伪属性:size/isEmpty/iterator/keys/values/next/hasNext。
<s:set value="#{'lhm':96,'zxx':93,'xpc':97}" var="map"/>
<s:if test="#map.size > 0">
<s:property value="#map"/>
</s:if>
|
4.集合的投影与过滤
投影就是拿着集合中的每个元素去进行运算,各个元素运算的结果组成一个新集合,新集合中的元素个数与原始集合中的元素个数相同。
<s:property value="persons.{name}"/>
<s:property value="{5,3,2}.{#this*2}"/>
|
过滤就是拿着集合中的每个元素去进行布尔运算,运算的结果为true,则将该元素保存到新集合中去。
?:获得所有符合逻辑的元素。
<s:property value="{5,3,2,0}.{? #this }"/>
|
^:获得符合逻辑的第一个元素。
<s:property value="{5,3,2,0}.{^ #this>3 }"/>
|
$:获得符合逻辑的最后一个元素。
<s:property value="{5,3,2,0}.{$ #this>2 }"/>
|
5.类型转换
转换成boolean类型:
-
整数0转换为fals
-
值为0的字符转化为false
-
Null对象转化为false
投影和选择操作符(e1.{e2} and e1.{?e2})里面的内容会被转换成集合
Map会被转化成其values属性返回的集合
数字会被转换成从0开始到比该数字小1的所有数字的集合。
单个对象被转换成仅仅只含有该对象的集合。
五、在配置文件中使用OGNL
在下堂课中会做总结!