OGNL & ValueStack&…

h1. 一个例子
请看下面的需求,假设有如下用户对象模型:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. public interface User {   
  2.     public String getName();   
  3.     public Date getRegisterDate();   
  4.     public Customer getCustomer();   
  5. }   
  6. public interface Customer {   
  7.     public String getId();   
  8.     public String getName();   
  9.     public boolean isVip();   
  10. }   
  11. public interface EntCustomer extends Customer {   
  12.     public String getTrustId(); // 组织机构代码证号   
  13.  
public interface User {
    public String getName();
    public Date getRegisterDate();
    public Customer getCustomer();
}
public interface Customer {
    public String getId();
    public String getName();
    public boolean isVip();
}
public interface EntCustomer extends Customer {
    public String getTrustId(); // 组织机构代码证号
}

对于给定的用户jack,且该用户所属客户是企业客户,那么我们如何获取该用户的姓名?如何获取用户所属客户的名称?如何判断该用户所属客户是否是VIP客户?如何取jack所属企业的组织机构代码证号?

* 采用java代码的方式,我们可以用如下的API调用得到所需信息:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. jack.getName();   
  2. jack.getCustomer().getName();   
  3. jack.getCustomer().isVip();   
  4. ((EntCustomer)jack.getCustomer()).getTrustId();  
jack.getName();
jack.getCustomer().getName();
jack.getCustomer().isVip();
((EntCustomer)jack.getCustomer()).getTrustId();


* 但我们现在在讲述OGNL,因此通过采用OGNL,我们可以用如下方式取得我们所需要的信息:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. jack.name   
  2. jack.customer.name   
  3. jack.vip   
  4. jack.customer.trustId  
jack.name
jack.customer.name
jack.vip
jack.customer.trustId

由此我们可以看到OGNL的表达方式与java表达方式有以下几点不同:
** 不需关注对象类型,不需进行类型转换
** 表达方式更简短和直观

OGNL表达式最大的优点就是:*简单* 和 *直观*,你不这样认为吗? 如果你觉得上面的表达式还不够简单和直观,那我们再来看:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. name  
name

这也是一个OGNL表达式,也就是取姓名!简单吗?至少足够直观了吧:)

h1. 基本概念
我们前面看到了OGNL的一个最简单的例子,事实上OGNL确实很简单,如果能理解上面那个例子的用法,那么我们就掌握了OGNL的80%的用法了。
上面的例子虽然简单,但其中却含有OGNL的两个最基本的概念:*表达式(expression)* 和 *上下文(context)*,我们先看*表达式*。

h3. 表达式
OGNL就是表达式!它能让我们用简洁直观的语法表达我们的想法,如同上面的例子一般。简洁直观就是表达式的最大优点!我们知道表达式总是有一个结果,也就是说表达式总是会求值出一个结果,这个结果可能是一个字符串(如名称、组织机构代码证号等),或者是一个布尔值(如是否是VIP客户等),至于这个结果要怎么使用,那就是我们自己来决定的了。

h3. 上下文(context)
表达式的概念,我相信很好理解,但什么是上下文(context)?简单来说上下文就是环境,表达式求值的环境!还是不理解吗?我们来看一个例子:
还是上面最后那个例子:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. name  
name
细心的你是否会问,这个表达式要取谁的姓名呢?OK,很好!这就是环境,"谁"就存在于环境之中,也就是存在上下文之中。对于不同的环境/上下文,相同的表达式会有不同的结果!而环境/上下文的实质是什么呢?就是一组带名称的对象集合。
引用
思考:表达环境或上下文这个概念的最好的数据结构是什么?


h3. OGNL上下文概念详解
我们前面说上下文就是一组名称-对象对的集合,如下图所示就是一个简单的上下文:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. user ---> User(name:"jack", ...)   
  2. request ---> HttpServletRequest(header: ...)  
user ---> User(name:"jack", ...)
request ---> HttpServletRequest(header: ...)

那么在上面的环境中,我们可以有如下的OGNL表达式:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. #user.name // 取用户的姓名   
  2. #user.age // 取用户年龄   
  3. #user.birthday // 取用户生日   
  4. #user.customer.name // 取用户所属客户的名称   
  5. #request.parameters // 取请求参数  
#user.name // 取用户的姓名
#user.age // 取用户年龄
#user.birthday // 取用户生日
#user.customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数

请注意上面表达式中的"#user"和"#request"的用法,"#"表示访问环境/上下文中的对象。

现在可以很方便地访问环境中的对象了,那么如果你比较懒惰的话(记住:在程序员群体,懒惰是褒义词!),你是否觉得访问用户的姓名,年龄,生日,等等其它属性如果全部要使用"#user"来访问会不会太麻烦了呢?OK,ONGL的设计者早就考虑了这个问题,我们可以指定user为环境中的特权对象,访问该对象可以不需要使用#user的方式,如下所示代码与上面的完全等价,当然,前提是要预先指定user为特权对象:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. name // 取用户的姓名   
  2. age // 取用户年龄   
  3. birthday // 取用户生日   
  4. customer.name // 取用户所属客户的名称   
  5. #request.parameters // 取请求参数  
name // 取用户的姓名
age // 取用户年龄
birthday // 取用户生日
customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数


我们上面所说的"特权对象"在OGNL中称为"根对象"(root)

h3. 小结
综上所述,理解OGNL表达式的关键是理解其上下文的概念,因为OGNL的上下文概念中引入了"根对象"的概念,所以初学者往往会在这里迷失方向。

引用

OGNL的中文全称是对象图导航语言,也就是说OGNL是一门语言,如同java是一门语言一样。你是否会认为OGNL的作者太夸张了,竟敢把表达式谎称为语言?不,OGNL的语法确实非常简洁,OGNL的代码(我没有说表达式,因为代码是和语法相匹配的词语)通常不会换行,这意味着我们不可能把OGNL的代码写得很长,但是,这并不意味着OGNL的表达能力很弱。事实上,OGNL的语法设计非常简洁,但其功能却相当强大,如果你有兴趣,可以深入阅读OGNL参考手册的集合与lambda章节。


慢着,事情还未至此结束!struts2对OGNL中的上下文的概念又定义了新的含义,且听我慢慢道来!

h3. struts2中的OGNL上下文
struts2对OGNL上下文的概念又做了进一步扩充,在struts2中,OGNL上下文通常如下所示:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. |   
  2.                      |--request   
  3.                      |   
  4.                      |--application   
  5.                      |   
  6.        context map---|--OgnlValueStack(root) user, action, OgnlUtil, ... ]   
  7.                      |   
  8.                      |--session   
  9.                      |   
  10.                      |--attr   
  11.                      |   
  12.                      |--parameters  
|
                     |--request
                     |
                     |--application
                     |
       context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
                     |
                     |--session
                     |
                     |--attr
                     |
                     |--parameters

我们可以使用"#requet"访问HttpServletRequest对象, "#session"访问HttpSession对象,但请注意"根对象"是什么?是ValueStack!
那么ValueStack是什么?值栈。也就是一组对象的堆栈。也就是说,在struts2中,根对象不是我们通常的一个对象,而是一组对象。我们可以push新的对象到值栈中,也可以弹出值栈的栈顶对象。如上图所示,假设我们将user对象push到值栈中,那么如下的表达式将与之前我们见过的表达式一样,具有相同的结果:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. name // 取用户的姓名   
  2. age // 取用户年龄   
  3. birthday // 取用户生日   
  4. customer.name // 取用户所属客户的名称   
  5. #request.parameters // 取请求参数  
name // 取用户的姓名
age // 取用户年龄
birthday // 取用户生日
customer.name // 取用户所属客户的名称
#request.parameters // 取请求参数

也就是说,我们使用name这个表达式的时候,ONGL会取"根对象"的name属性,但现在根对象是ValueStack!那么访问ValueStack的name属性意味着什么呢?这意味着: ValueStack会先查看栈顶元素是否有name属性,如果有就返回该属性值,否则取出栈顶下的元素,继续查看,直到栈底为止。

以上就是OGNL表达式的核心概念,你理解了吗?下一步,你需要了进一步了解OGNL的语法,以发掘其更强大的功能!
介绍struts2中几个核心的组件ActionContext、ValueStack、OGNL表达式

1、ActionContext

当struts2框架接收到一个HTTP请求时,它立刻创建一个ActionContext、ValueStack、Action对象

ActionContext里有6个对象,分别是valueStack、parameters、request、session、application、attr

一个OGNL表达式,必须选择ActionContext中的一个对象作为根对象(root),默认情况下,是选择valueStack作为根对象,如果需要使用另外5个对象作为根对象,需要加上#前缀

例如:
Java代码 复制代码  收藏代码OGNL <wbr>& <wbr>ValueStack <wbr>ActionContext入门
  1. "#session.xxx" />  
 
 
如果不加#前缀,则默认使用valueStack作为根对象,这也是最常见的情况,即#valueStack.xxx,相当于xxx

2、ValueStack(值栈)

ValueStack中可以存储很多对象,它的一个特性是,它是一个虚拟对象,它可以将自己持有的对象的属性,当成是自己的属性

比如说,ValueStack中有一个Action对象,而Action对象有一个name字段。那么当用OGNL表达式取name的值的时候,不需要${action.name},而是可以直接${name}

ValueStack是一个栈的数据结构(FILO),最后进入值栈的对象,总是在ValueStack的栈顶,这个结论很重要,因为栈顶的元素的值,会覆盖栈底的同名元素的值。

比如说,ValueStack的栈底是一个Action对象,持有一个name字段;栈顶是一个Model对象,也持有一个name字段,那么用${name},取出来的永远是Model对象的name字段,Action对象的name字段是不可见的

3、OGNL表达式

这个可以分为2种场景,一种是在标签的属性里(比如),一种是在jsp页面的其他地方

在标签的属性里时,要看这个属性定义的类型是什么,如果是string类型,那么属性的值会被当做普通的string,如果不是string类型,那么属性的值会被直接当成OGNL的表达式

比如说,这个标签的value属性的类型是object,那么这个value的值,就会被直接作为OGNL表达式进行解析

如果想在string类型的属性中使用OGNL表达式,就需要加上${}或者%{}

在jsp页面的其他地方时(即不在标签内部),任何情况下都会当成string来处理,这时候如果想使用OGNL表达式,也需要加上${}或者%{}

4、关于${}和%{}的区别

根据文档里的描述,OGNL表达式应该用%{}来表示。可是我在实际应用中,基本不会使用标签,并且都是用${}来取值的,也没有发现任何不妥,不知道是不是版本的原因
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值