IBMSoftwareGroup®©2008IBMCorporationIBMSoftwareGroup第四章:Struts2中的OGNL主讲:刘雷IBMSoftwareGroupIBMSoftwareGroup学习目标掌握OGNL表达式的使用掌握值栈的概念掌握struts2对OGNL表达式的增强IBMSoftwareGroupIBMSoftwareGroupOGNL简介Struts2的一个关键特性就是它可以对Action携带的数据进行读写访问,例如在前面我们在表单中使用user.username指定数据传递给Action的user对象的username属性,在s:property元素中使用user.username来获取用户的名字,这是通过表达式语言(ExpressionLanguage,EL)来实现的,这种表达式语言就是OGNL。OGNL的全称是ObjectGraphNavigationLanguage(对象图导航语言),它是一种强大的表达式语言,让你通过简单一致的表达式语法来读取和设置Java对象的属性值,调用对象的方法,遍历整个对象的结构图,实现字段类型转换等功能。IBMSoftwareGroupIBMSoftwareGroup为什么要使用OGNL视图层的表达式语言通常是用来简化数据的访问操作,取代Java脚本代码,提供更清晰的视图层实现。例如,在JSP页面中使用JSP2.0内置的表达式语言获取user对象的username属性,可以简写为${user.username},如果换作Java脚本代码,则需要写为:%@pagelanguage=javaimport=java.util.*,com.bim.pojo.UserpageEncoding=gbk%%Useruser=(User)request.getAttribute(user);Stringusername=user.getUsername();out.print(username);%哪一种方法更为简捷,相信大家已经一目了然了。既然JSP2.0已经内置了一种表达式语言,那么为什么还要使用OGNL呢?相对于其它的表达式语言而言,OGNL的功能更为强大,它提供了很多高级而必须的特性,例如强大的类型转换功能,静态或实例方法的执行,跨集合投影(projection),以及动态lambda表达式定义等。IBMSoftwareGroupIBMSoftwareGroupOGNL基础OGNL表达式的计算都是围绕OGNL上下文来进行的,OGNL上下文实际上就是一个Map对象,由ognl.OgnlContext类(实现了java.util.Map接口)来表示。OGNL上下文可以包含一个或多个JavaBean对象,在这些对象中有一个是特殊的,这个对象就是上下文的根(root)对象。如果在写表达式的时候,没有指定使用上下文中的哪一个对象,那么根对象将被假定为表达式所依据的对象。计算OGNL表达式的示例代码(一):IBMSoftwareGroupIBMSoftwareGroup计算OGNL表达式的示例代码(二)IBMSoftwareGroupIBMSoftwareGroup计算OGNL表达式的示例代码(三)IBMSoftwareGroupIBMSoftwareGroup说明OgnlContext就是一个Map,在Map中保存值,需要指定键(key),你在写表达式的时候使用的就是键名,而不是对象名,这一点一定要注意。在Ognl上下文中,只能有一个根对象,如果你访问根对象,那么在写表达式的时候,直接写对象属性(property)就可以了;否则,你需要使用”#key”前缀,例如表达式:#manager.name.另外需要注意的是,OGNL表达式中的属性(property)是JavaBean的属性,而不是类中的实例变量。IBMSoftwareGroupIBMSoftwareGroup再看一个简单示例请看下面的需求,假设有如下用户对象模型:Java代码publicinterfaceUser{publicStringgetName();publicDategetRegisterDate();publicCustomergetCustomer();}publicinterfaceCustomer{publicStringgetId();publicStringgetName();publicbooleanisVip();}publicinterfaceEntCustomerextendsCustomer{publicStringgetTrustId();//组织机构代码证号}IBMSoftwareGroupIBMSoftwareGroup问题及解决方案(一)对于给定的用户jack,且该用户所属客户是企业客户,那么我们如何获取该用户的姓名?如何获取用户所属客户的名称?如何判断该用户所属客户是否是VIP客户?如何取jack所属企业的组织机构代码证号?*采用java代码的方式,我们可以用如下的API调用得到所需信息:Java代码jack.getName();jack.getCustomer().getName();jack.getCustomer().isVip();((EntCustomer)jack.getCustomer()).getTrustId();IBMSoftwareGroupIBMSoftwareGroup问题及解决方案(二)*但我们现在在讲述OGNL,因此通过采用OGNL,我们可以用如下方式取得我们所需要的信息:Java代码jack.namejack.customer.namejack.customer.vipjack.customer.trustId由此我们可以看到OGNL的表达方式与java表达方式有以下几点不同:**不需关注对象类型,不需进行类型转换**表达方式更简短和直观OGNL表达式最大的优点就是:*简单*和*直观*,你不这样认为吗?如果你觉得上面的表达式还不够简单和直观,那我们再来看:Java代码name这也是一个OGNL表达式,也就是取姓名!简单吗?至少足够直观了吧IBMSoftwareGroupIBMSoftwareGroupOGNL基本概念我们前面看到了OGNL的一个最简单的例子,事实上OGNL确实很简单,如果能理解上面那个例子的用法,那么我们就掌握了OGNL的80%的用法了。上面的例子虽然简单,但其中却含有OGNL的两个最基本的概念:*表达式(expression)*和*上下文(context)*,我们先看*表达式*。表达式OGNL就是表达式!它能让我们用简洁直观的语法表达我们的想法,如同上面的例子一般。简洁直观就是表达式的最大优点!我们知道表达式总是有一个结果,也就是说表达式总是会求值出一个结果,这个结果可能是一个字符串(如名称、组织机构代码证号等),或者是一个布尔值(如是否是VIP客户等),至于这个结果要怎么使用,那就是我们自己来决定的了。上下文(context)表达式的概念,我相信很好理解,但什么是上下文(context)?简单来说上下文就是环境,表达式求值的环境!还是不理解吗?我们来看一个例子:还是上面最后那个例子:Java代码name细心的你是否会问,这个表达式要取谁的姓名呢?OK,很好!这就是环境,谁就存在于环境之中,也就是存在上下文之中。对于不同的环境/上下文,相同的表达式会有不同的结果!而环境/上下文的实质是什么呢?就是一组带名称的对象集合。IBMSoftwareGroupIBMSoftwareGroupOGNL上下文概念详解OGNL上下文概念详解我们前面说上下文就是一组名称-对象对的集合,如下图所示就是一个简单的上下文:Java代码user---User(name:jack,...)request---HttpServletRequest(header:...)那么在上面的环境中,我们可以有如下的OGNL表达式:Java代码#user.name//取用户的姓名#user.age//取用户年龄#user.birthday//取用户生日#user.customer.name//取用户所属客户的名称#request.parameters//取请求参数请注意上面表达式中的#user和#request的用法,#表示访问环境/上下文中的对象。IBMSoftwareGroupIBMSoftwareGroupOGNL上下文概念详解现在可以很方便地访问环境中的对象了,那么如果你比较懒惰的话(记住:在程序员群体,懒惰是褒义词!),你是否觉得访问用户的姓名,年龄,生日,等等其它属性如果全部要使用“#user”来访问会不会太麻烦了呢?OK,OGNL的设计者早就考虑了这个问题,我们可以指定user为环境中的特权对象,访问该对象可以不需要使用#user的方式,如下所示代码与上面的完全等价,当然,前提是要预先指定user为特权对象:Java代码name//取用户的姓名age//取用户年龄birthday//取用户生日customer.name//取用户所属客户的名称#request.parameters//取请求参数我们上面所说的特权对象在OGNL中称为根对象(root)综上所述,理解OGNL表达式的关键是理解其上下文的概念,因为OGNL的上下文概念中引入了“根对象”的概念。IBMSoftwareGroupIBMSoftwareGroupstruts2中的OGNL上下文struts2中的OGNL上下文struts2对OGNL上下文的概念又做了进一步扩充,在struts2中,OGNL上下文通常如下所示:Java代码|--application||--session|contextmap---|--OgnlValueStack(root)[user,action,OgnlUtil,...]||--request||--parameters||--attrIBMSoftwareGroupIBMSoftwareGroup我们可以使用#request访问HttpServletRequest对象,#session访问HttpSession对象,但请注意根对象是什么?是ValueStack!那么ValueStack是什么?值栈。也就是一组对象的堆栈。也就是说,在struts2中,根对象不是我们通常的一个对象,而是一组对象。我们可以push新的对象到值栈中,也可以弹出值栈的栈顶对象。假设我们将user对象push到值栈中,那么如下的表达式将与之前我们见过的表达式一样,具有相同的结果:Java代码name//取用户的姓名age//取用户年龄birthday//取用户生日customer.name//取用户所属客户的名称#request.parameters//取请求参数也就是说,我们使用name这个表达式的时候,ONGL会取根对象的name属性,但现在根对象是ValueStack!那么访问ValueStack的name属性意味着什么呢?这意味着:ValueStack会先查看栈顶元素是否有name属性,如果有就返回该属性值,否则取出栈顶下的元素,继续查看,直到栈底为止。以上就是OGNL表达式的核心概念,你理解了吗?下一步,你需要了进一步了解OGNL的语法,以发掘其更强大的功能!IBMSoftwareGroupIBMSoftwareGroup理解Struts2中的ValueStackValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。OgnlValueStack类的主要属性关系图如下|--application||--sessioncontextmap(OgnlValueStack属性)--||--valuestack(OgnlValueStack的root属性,实际是个A