<span style="font-family: 微软雅黑, 'Times New Roman'; font-size: 24px; line-height: 24px; background-color: rgb(255, 255, 255);">1. OGNL</span>1.1. OGNL介绍1.1.1. 什么是OGNL
Object Graph Navigation Language,是一门功能强大的表达式语言,类似于EL。
1.1.2. 为什么用OGNL
OGNL表达式功能很强大,后面我们会重点阐述。而Struts2默认采用OGNL表达式访问Action的数据,实际上是通过ValueStack用封装后的OGNL来访问的Action。
1.1.3. OGNL原理
OGNL是独立的开源组件,Struts2对其进行了改造及封装,要想了解Struts2中OGNL的运行原理,需参考ValueStack。
1.2. OGNL用法1.2.1. Struts2显示标签
Struts2中,OGNL表达式要结合Struts2标签来访问数据,即OGNL表达式要写在Struts2标签内,因此我们先来介绍第一个Struts2的标签——显示标签。
1、语法
<s:property value="OGNL"/>
2、解释
该标签的作用是根据OGNL表达式访问Action,并将取出的数据替换标签本身。如下图,实际上该标签类似于EL表达式中的${ }。
图-1
1.2.2. 2个常用的OGNL表达式
OGNL表达式一共有8种使用方法,其中前2种要求大家必须掌握,是经常要用到的方式,后6种了解即可,下面我们来介绍这2种必须掌握的OGNL。
1、访问基本属性
2、访问实体对象
可以看出,这两种访问Action的方式实际上与EL表达式用法完全一致,是最常用的,也是最容易掌握的2种方式。
1.2.3. 6个需要了解的OGNL表达式2. ValueStack2.1. ValueStack介绍2.1.1. 什么是ValueStack
ValueStack是Struts2中,Action向页面传递数据的媒介,ValueStack封装了Action的数据,并允许JSP通过OGNL来对其进行访问。
2.1.2. ValueStack原理
如下图,Struts2使用了ValueStack对OGNL组件进行了封装,其封装的实际上是改造后的OGNL,当然我们不关注改造的过程,只需要掌握改造后的OGNL在ValueStack中的运行原理即可。
ValueStack中首先封装了OGNL解析引擎,用于解析传入的OGNL表达式,其目的就是在页面上以标签+字符串的方式访问Java对象,从而降低了页面代码的开发难度,提升了页面代码的维护效率,而OGNL表达式传入引擎的时机我们在后面的Action基本原理中会讲到。
OGNL引擎可以访问2种类型的对象,一种是栈类型,另一种是Map类型。
1、栈
2、Map
总体来说,由于context对象中的数据固定了,因此对context对象的访问比较简单和直接。而栈中数据是会有所变化的,并且访问时也是自顶向下动态访问的,因此我们在学习ValueStack时要重点关注栈的结构和变化。
图-2
2.2. 访问ValueStack2.2.1. 1、通过debug标签观察其结构
ValueStack的原理是比较抽象的,对于其结构的理解也不够直观。对于这种情况,Struts2提供了一个调试标签,可以用于观察ValueStack的结构,该标签的效果如下图。
图-3
可以看出,该标签会在页面上生成一个debug链接,点击后会展开ValueStack的结构描述内容,其中包括栈的结构及数据、context对象的结构及数据。
该标签的语法比较简单,即<s:debug/>。其作用仅仅是用于调试的,是给开发人员来使用的,当项目提交测试以及上线时,要删除该标记。
值得注意的是,该标签存在互斥性,在页面上如果写多个调试标签,实际上只有第一个是准确的,其他的标签内容有问题。但鉴于该标签仅仅是给开发人员调试使用的,因此我们在了解这个规则的前提下,只要保持页面上只有一个调试标签即可正常使用。
2.2.2. 2、输出栈顶
前面我们介绍了Struts2的显示标签,即<s:property value="OGNL"/>。这个标签有一种特殊的用法,可以直接输出栈顶的内容,语法为<s:property />。
也就是说,将显示标签的value属性去掉,那么该标签将默认输出栈顶的内容。
2.2.3. 3、访问context对象
Context对象是一个Map类型的对象,我们使用OGNL访问它的方式是固定的,即#key,返回的值是当前key在Map中对应的值。
2.2.4. 4、迭代集合
我们可以使用Struts2的迭代标签结合着OGNL,来迭代Action中的集合属性,迭代集合标签的语法如下:
<s:iterator value="users"><s:property value="userName"/></s:iterator>
users是OGNL表达式,自顶向下访问ValueStack栈中root对象的users属性,这里会从栈顶的Action对象取到该集合属性(List<User> users)值 。
需要注意的是,在迭代的过程中,ValueStack的栈顶会发生变化,循环变量User会被压入栈顶,此时Action被压到栈的第二位,即栈顶由由原来的Action变为循环变量User,如下图
图-4
userName是OGNL表达式,自顶向下访问ValueStack栈中root对象的userName属性,这里会从栈顶的User对象取到userName属性值。
可以看出,这种动态访问栈中数据的方式,对我们的理解增加了一些难度,但是却可以简化循环内部的OGNL表达式,因为在循环时栈顶即为循环变量,我们以它为root写OGNL不用关注变量的名字了,而这部分代码在项目中是十分频繁出现的,因此牺牲一些理解难度的代价是完全值得的。
2.2.5. 5、按数字迭代
有时候我们也需要按照数字的方式进行迭代,比如资费列表的分页功能。对于这种方式,Struts2也有对应的标签支持,按照数字迭代的标签语法如下:<s:iterator begin="from" end="to" var="k"><s:property value="#k"/></s:iterator>
from/to是OGNL表达式,自顶向下访问ValueStack栈中root对象的from/to属性,这里会从栈顶的Action对象取到属性(int from=1, to=3)值。
与迭代集合一样,按数字迭代时,栈顶也会变为循环变量。即在循环过程中,循环变量会被压入栈顶,此时Action被压到栈的第二位,如下图
图-5
鱼儿爱美,不仅需要鳞甲之美。还需要浮沉活泼之美。