引言:众所周知,在mvc中,数据是在各个层次之间进行流转是一个不争的事实。而这种流转,也就会面临一些困境,这些困境,是由于数据在不同世界中的表现形式不同而造成的:

  1. 数据在页面上是一个扁平的,不带数据类型的字符串,无论你的数据结构有多复杂,数据类型有多丰富,到了展示的时候,全都一视同仁的成为字符串在页面上展现出来。

  2. 数据在Java世界中可以表现为丰富的数据结构和数据类型,你可以自行定义你喜欢的类,在类与类之间进行继承、嵌套。我们通常会把这种模型称之为复杂的对象树。

  此时,如果数据在页面和Java世界中互相流转传递,就会显得不匹配。所以也就引出了几个需要解决的问题:

  1. 当数据从View层传递到Controller层时,我们应该保证一个扁平而分散在各处的数据集合能以一定的规则设置到Java世界中的对象树中去。同时,能够聪明的进行由字符串类型到Java中各个类型的转化。

  2. 当数据从Controller层传递到View层时,我们应该保证在View层能够以某些简易的规则对对象树进行访问。同时,在一定程度上控制对象树中的数据的显示格式。

如果我们稍微深入一些来思考这个问题,我们就会发现,解决数据由于表现形式的不同而发生流转不匹配的问题对我们来说其实并不陌生。同样的问题会发生在Java世界与数据库世界中,面对这种对象与关系模型的不匹配,我们采用的解决方法是使用ORM框架,例如Hibernate,iBatis等等。那么现在,在Web层同样也发生了不匹配,所以我们也需要使用一些工具来帮助我们解决问题。为了解决数据从View层传递到Controller层时的不匹配性,Struts2采纳了XWork的一套完美方案。并且在此的基础上,构建了一个完美的机制,从而比较完美的解决了数据流转中的不匹配性。相信大家看到这一定猜出来了这里的完美方案和完美机制了。对,这就是OGNL方案和OGNLValueStack机制

 基本概念

OGNL(Object Graph Navigation Language),是一种表达式语言。使用这种表达式语言,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。既然OGNL那么强大,那么让我们一起来研究一下他的API,看看如何使用OGNL.

OGNL的API看起来就是两个简单的静态方法:

public static Object getValue( Object tree, Map context, Object root ) throws OgnlException;  public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException     

我们可以看到,简单的API,就已经能够完成对各种对象树的读取和设值工作了。这也体现出OGNL的学习成本非常低。需要特别强调进行区分的,是在针对不同内容进行取值或者设值时,OGNL表达式的不同。Struts2 Reference 写道:

The Framework uses a standard naming context to evaluate OGNL expressions. The top level object dealing with OGNL is a Map (usually referred as a context map or context). OGNL has a notion of there being a root (or default) object within the context. In expression, the properties of the root object can be referenced without any special "marker" notion. References to other objects are marked with a pound sign (#).

针对上面的话,我们可以简单的理解为下面两点:

A) 针对根对象(Root Object)的操作,表达式是自根对象到被访问对象的某个链式操作的字符串表示。

B) 针对上下文环境(Context)的操作,表达式是自上下文环境(Context)到被访问对象的某个链式操作的字符串表示,但是必须在这个字符串的前面加上#符号,以表示与访问根对象的区别。

OGNL三要素:

很多人习惯上把传入OGNL的API的三个参数,称之为OGNL的三要素。OGNL的操作实际上就是围绕着这三个参数而进行的。

看下面一段测试代码:

 Ognl.setValue("department.name", user2, "dev");      System.out.println(user2.getDepartment().getName());     Ognl.setValue(Ognl.parseexpression_r("department.name"), context, user2, "otherDev");     System.out.println(user2.getDepartment().getName());    

1. 表达式(Expression)

  表达式是整个OGNL的核心,所有的OGNL操作都是针对表达式的解析后进行的。表达式会规定此次OGNL操作到底要干什么。我们可以看到,在上面的测试中,name、department.name等都是表达式,表示取name或者department中的name的值。OGNL支持很多类型的表达式,之后我们会看到更多。

2. 根对象(Root Object)

  根对象可以理解为OGNL的操作对象。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。

  在上面的测试代码中,user就是根对象。这就意味着,我们需要对user这个对象去取name这个属性的值(对user这个对象去设置其中的department中的name属性值)。

3. 上下文环境(Context)

  有了表达式和根对象,我们实际上已经可以使用OGNL的基本功能。例如,根据表达式对根对象进行取值或者设值工作。不过实际上,在OGNL的内部,所有的操作都会在一个特定的环境中运行,这个环境就是OGNL的上下文环境(Context)。说得再明白一些,就是这个上下文环境(Context),将规定OGNL的操作“在哪里干”。

  OGNL的上下文环境是一个Map结构,称之为OgnlContext。上面我们提到的根对象(Root Object),事实上也会被加入到上下文环境中去,并且这将作为一个特殊的变量进行处理,具体就表现为针对根对象(Root Object)的存取操作的表达式是不需要增加#符号进行区分的。

OgnlContext不仅提供了OGNL的运行环境。在这其中,我们还能设置一些自定义的parameter到Context中,以便我们在进行OGNL操作的时候能够方便的使用这些parameter。不过正如我们上面反复强调的,我们在访问这些parameter时,需要使用#作为前缀才能进行。

OGNL表达式实现原理

Struts 2中的OGNL Context实现者为ActionContext,它结构示意图如下:

当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。访问上下文(Context)中的对象需要使用#符号标注命名空间,如#application、#session,另外OGNL会设定一个根对象(root对象),在Struts2中根对象就是ValueStack(值栈) 。如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。

在struts2中,根对象ValueStack的实现类为OgnlValueStack,该对象不是我们想像的只存放单个值,而是存放一组对象。在OgnlValueStack类里有一个List类型的root变量,就是使用他存放一组对象 |--request |--application context ------|--OgnlValueStack root变量[action, OgnlUtil, ... ] |--session |--attr |--parameters,在root变量中处于第一位的对象叫栈顶对象。通常我们在OGNL表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下访问,直到找到为止。 大家注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>

由于ValueStack(值栈)是Struts 2中OGNL的根对象,如果用户需要访问值栈中的对象,在JSP页面可以直接通过下面的EL表达式访问ValueStack(值栈)中对象的属性: ${foo} //获得值栈中某个对象的foo属性。如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀。

application对象:用于访问ServletContext,例如#application.userName或者#application['userName'],相当于调用ServletContext的getAttribute("username")。
session对象:用来访问HttpSession,例如#session.userName或者#session['userName'],相当于调用session.getAttribute("userName")。
request对象:用来访问HttpServletRequest属性(attribute)的Map,例如#request.userName或者#request['userName'],相当于调用request.getAttribute("userName")。

parameters对象:用于访问HTTP的请求参数,例如#parameters.userName或者#parameters['userName'],相当于调用request.getParameter("username")。
attr对象:用于按page->request->session->application顺序访问其属性。

好了,基本概念和原理占时先介绍到这,下篇博客则着重说一下OGNL表达式的基本语法和用法,谢谢大家一直以来的支持。

转载于:https://blog.51cto.com/javacsh/1129224

Java程序员从笨鸟到菜鸟之(四十八)细谈struts2(十)ognl概念和原理详解相关推荐

  1. Java程序员从笨鸟到菜鸟全部博客目录

    本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.NET/csh624366188 欢迎关注微信账号:java那些事:csh624366188.每天一篇java相关的文章 大 ...

  2. Java程序员从笨鸟到菜鸟之(五)java开发常用类(包装,数字处理集合等)(下)...

     本文来自:曹胜欢博客专栏.转载请注明出处:http://blog.csdn.net/csh624366188 写在前面:由于前天项目老师建设局的项目快到验收阶段,所以,前天晚上通宵,昨天睡了大半天, ...

  3. Java程序员从笨鸟到菜鸟之(一百零九)一步一步学习webservice(三)开发第一个基于XFire的webservice

    在日常开发中,常用的webservice引擎主要有Axis,axis2,Xfire以及cxf(Xfire的升级版).现在只有axis2和cxf官方有更新.今天我们先以一个比较老的引擎为例.来讲诉一下w ...

  4. Java程序员从笨鸟到菜鸟之(一百零八)一步一步学习webservice(二)webservice基本原理

    本来这第二篇打算讲解"开发第一个基于XFire的webservice"的内容来着.但是想想.开发实例只是局限于了会用的层面上.如果想真正的理解webservice还是需要挖掘其原理 ...

  5. Java程序员从笨鸟到菜鸟之(五)java开发常用类(包装,数字处理集合等)(下)

    写在前面:由于前天项目老师建设局的项目快到验收阶段,所以,前天晚上通宵,昨天睡了大半天,下午我们宿舍聚会,所以时间有点耽误,希望大家见谅 上接: Java程序员从笨鸟到菜鸟之(四)java开发常用类( ...

  6. Java程序员从笨鸟到菜鸟之(一百零六)java操作office和pdf文件(四)页面列表导出cvs,excel、pdf报表.

    在平常的开发中我们常常遇到不仅仅只是导出excel报表的情况.有时候也需要导出pdf或者CSV报表.其实原理都差不多.刚开始本来不打算也这篇博客介绍这个的.感觉这篇博客和前面的博客有点雷同.原理基本都 ...

  7. Java程序员从笨鸟到菜鸟之(一百零四)java操作office和pdf文件(二)利用POI实现数据导出excel报表...

    在上一篇博客中,我们简单介绍了java读取word,excel和pdf文档内容 ,但在实际开发中,我们用到最多的是把数据库中数据导出excel报表形式.不仅仅简单的读取office中的数据.尤其是在生 ...

  8. Java程序员从笨鸟到菜鸟之(序言)+全部链接

    http://blog.csdn.net/csh624366188 大学上了一年半,接触java也一年半了,虽然中间也有其他东西的学习,但是还是以java为主路线,想想这一年半,除去前半年几乎全玩了, ...

  9. Java程序员由笨鸟到菜鸟 电子版书正式发布 欢迎大家下载

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 欢迎关注 ...

  10. java程序员从笨鸟到菜鸟_Java程序员从笨鸟到菜鸟之(十四)Html基础积累总结(上)...

    [新朋友]点击标题下面(↑)蓝色字"Java那些事"关注 [老朋友]点击右上角,转发或分享本页面内容 这是我以前写的<java程序员由笨鸟到菜鸟>系列博客,每天更新一篇 ...

最新文章

  1. Nested Mappings
  2. idea中文乱码问题
  3. 别再折腾开发环境了,一劳永逸的搭建方法
  4. 作业帮电脑版在线使用_在线K12赛道六虎争霸:猿辅导、作业帮又宣布新一轮融资...
  5. TLS/SSL协议工作原理
  6. Java Web学习总结(43)—— Restful API 版本控制
  7. 使用Builder模式创建复杂可选参数对象
  8. linux查询机器信息,linux_机器信息查询
  9. contact form 7如何设置placeholder让提示文字显示在输入框中
  10. 《程序员》: Andrew Ng谈Deep Learning
  11. GCN在交通流预测方面的相关文章
  12. word批量设置图片大小和对齐,使用宏定义
  13. 程序之父: Pascal之父:尼克劳斯·沃思(一)
  14. 【随缘侃史】蹈舞求生许敬宗
  15. 尚硅谷外卖项目笔记二
  16. C盘根目录下只能创建文件夹不能新建文件的解决办法
  17. 高清优质PPT模板20篇下载(商务型系列)
  18. vue是怎么实现数据响应式的?
  19. 贝恩资本联手华为22亿美元收购3Com
  20. MySQL 大作业实训考试题_2020系统综合实践 期末大作业 15组

热门文章

  1. 软件测试启航篇:测试的分类
  2. linux xampp图形界面,linux下安装xampp,XAMPP目录结构
  3. concurrentbag 删除_你知道吗?这样删除iPhone中的APP腾出的空间会更大
  4. linux vbox安装mac os,超简单的linux下virtualbox4.3.26安装配置黑苹果 OSX 10.9的办法
  5. mysql 锁怎么使用_MySQL锁的用法之行级锁
  6. pytorch 解压kaggle中的zgz文件
  7. linux+gpio+嵌入式,嵌入式Linux系统中对GPIO操作的方法总结
  8. mysql io 100_MySQL服务器 IO 100%的案例分析
  9. java如何集成dubbo_boot集成dubbo踩过的坑
  10. linux的相关网站,与Linux相关的一些网站