struts2 ognl 原理

OGNL(Object-Graph Navigation Language),大概可以理解为:对象图形化导航语言。是一种可以方便地操作对象属性的开源表达式语言(EL)。OGNL有如下特点: 

1、支持对象方法调用,形式如:objName.methodName(); 

2、支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路)]@[方法名| 值名],例如: 

@java.lang.String@format('foo%s', 'bar')或@tutorial.MyConstant@APP_NAME; 

3、支持赋值操作和表达式串联,例如: 

price=100, discount=0.8, calculatePrice(),这个表达式会返回80; 

4、访问OGNL上下文(OGNL context)和ActionContext; 

5、操作集合对象。  

 

Struts2内置了OGNL这是大家都知道的了,其实这种说话不严格,应该OGNL本身是一种很强大的表达式语言,XWork整合了OGNL的一部分并且将这部分内容做了相应的扩展,而Struts2又建立在XWork这个项目的基础之上,所以Struts2也就内置了OGNL这部分强大的功能。

        为了详细些一步一步做记录吧。

        先说OGNL,OGNL内部自己会维护一个OGNL上下文(OgnlContext)是一个实现了Map接口的对象,OGNL会将对象放在这个上下文中统一管理的,并且会有一个对象被指定为根对象(root)。假设有User和Customer这么两个类,分别有user和customer两个实例,这两个对象都有一个name属性,并且将user对象配置为唯一的根对象,那么就会有下面的情况:

#user.name                    返回user.getName();

#customer.name            返回customer.getName();

#name                            返回user.getName();        因为user对象为根对象

OGNL访问对象的基本机制就是这样的。

        在XWork中,ValueStack对象是上下文中的根对象,XWork中不仅可以根据表达式从ValueStack对象中获取对象,还可以获取对象的属性,XWork自己有一个强大的属性访问器,它可以自动搜索栈内的所有实体(从上到下),直到找到你所访问的对象和属性。假设ValueStack栈中有两个上面所说的user和customer两个对象,并且user是栈顶元素,customer在下面,就会有下面的情况:

name        返回user.getName();  因为user在栈顶,搜索器找到一个匹配的属性就不会继续找下去

如果希望访问靠后对象的属性XWork提供了索引访问ValueStack的方法:

[0].name         返回user.getName()

[1].name         返回customer.getName();

      1)  Struts2框架也没有对XWork中的OGNL做太大的修改,只是拿过来做了一下整合工作,Struts2将ActionContext设置为OGNL中的上下文,ValueStack依然是OGNL上下文中的根元素,大家知道application,session和request这几个对象也是在ActionContext中的,它们是和根对象ValueStack共存的,与根对象是无关的,就像刚开始说OGNL那个例子实在OGNL上下文中的两个平行对象,只不过ValueStack这个对象是这个OGNL上下文中的根对象(root)。如果要访问上下文中根对象中的对象和属性是不用加任何标记符号的,但是在访问application,session和request这几个范围内的对象和属性就要用"#"符号来告诉ognl不要在根对象中去搜索了,而是去我们指定的上下文中去找。

  2) Struts2总是把当前Action实例放置在值栈的栈顶。所以,在OGNL中引用Action中的属性也可以省略“#”。

 

用官方一个图来形象描述ValueStack和其他几个对象的关系:

                     |--request
                     |
                     |--application
                     |
            context map---|--OgnlValueStack(root)
                     |
                     |--session
                     |
                     |--attr
                     |
                     |--parameters

        基本原理就是这样了,举例验证理论。

1. ActionContext是OGNL的上下文,ValueStack对象是上下文中的根对象:

    随便找一个Action给JSP页面传东西过去的例子,然后在Action的execute()方法中加入以下代码:

    ActionContext.getContext().getValueStack().set("test", "love java");

    在目标JSP页面空白处加上如下代码:

    <s:label value="%{test}"></s:label>

再次跳转到该页面就可以看到love java出现在页面上了

2. 其他范围和ValueStack平行关系:

    还找刚才那个例子,在Action中加入

    request.setAttribute("test1", "love java1");

    在JSP页面加入代码:

    <s:label value="%{#request.test1}"></s:label>

这次跳转就可以看到love java1出现了

3. OGNL上下文的访问

    Action中加入代码:

    ActionContext.getContext().put("test2", "love java2");

    JSP页面中加入:

    <s:label value="%{#test}"></s:label>

看到love java2之后应该大彻大悟了吧,这里值得说一下就是在ActionContext.getContext().put("test2", "love java2")之后上下文结构:

                     |--request
                     |
                     |--application
                     |
            context map---|--OgnlValueStack(root)
                     |
                     |--session
                     |
                     |--attr
                     |
                     |--parameters
                     |
                     |
                     |--test2
                  
这样更清楚了吧!

总结:

    1.  在JSP页面中"%{"就表示OGNL表达式开始,"}"表示OGNL表达式结束

    2.  如果访问根对象中的对象和属性不用任何符号标志,如:%{Object.field}

    3.  访问特定Scope中的对象和属性用"#"来通知OGNL查询器,如:%{#request.object},当然OGNL很强大,在一个链中,前一个对象做为解释下一个对象的上下文。你可以任意扩展这个链,在链中也可以访问对象中的方法。

 

--------------------------------------------------------------------

使用OGNL表达式 

  1. OGNL要结合struts标签来使用。由于比较灵活,也容易把人给弄晕,尤其是“%”、“#”、“$”这三个符号的使用。由于$广泛应用于EL中,这里重点写%和#符号的用法。 
  2. 1、“#”符号有三种用途: 
  3. (1)、访问非根对象(struts中值栈为根对象)如OGNL上下文和Action上下文,#相当于ActionContext.getContext();下表有几个ActionContext中有用的属性: 
  4. 名称 
  5. 作用 
  6. 例子 
  7.  
  8. parameters 
  9. 包含当前HTTP请求参数的Map 
  10. #parameters.id[0]作用相当于request.getParameter("id") 
  11.  
  12. request 
  13. 包含当前HttpServletRequest的属性(attribute)的Map 
  14. #request.userName相当于request.getAttribute("userName") 
  15.  
  16. session 
  17. 包含当前HttpSession的属性(attribute)的Map 
  18. #session.userName相当于session.getAttribute("userName") 
  19.  
  20. application 
  21. 包含当前应用的ServletContext的属性(attribute)的Map 
  22. #application.userName相当于application.getAttribute("userName") 
  23.  
  24. 注:attr 用于按request > session > application顺序访问其属性(attribute),#attr.userName相当于按顺序在以上三个范围(scope)内读取userName属性,直到找到为止。用于过滤和投影(projecting)集合,如books.{?#this.price<100};构造Map,如#{'foo1':'bar1','foo2':'bar2'}。  
  25. (2)、用于过滤和投影(projecting)集合,如:books.{?#this.price>35    
  26. (3)、构造Map,如:#{'foo1':'bar1','foo2':'bar2'} 
  27. #{'foo1':'bar1','foo2':'bar2'}这种方式常用在给radio或select、checkbox等标签赋值上。如果要在页面中取一个map的值可以这样写: 
  28. <s:property value="#myMap['foo1']"/> 
  29. <s:property value="#myMap['foo1']"/> 
  30. 2、“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。 
  31. 这是一开始最让我不能理解的符号,原因是一些相关资源在表述时不太准备,经过一翻痛苦的探索,终于明白了它的用途。实际上就是让被理解为字符串的表达式,被真正当成ognl来执行。很有点类似javascript里面的eval_r()功能,例如: 
  32. var oDiv = eval_r("document.all.div"+index) 
  33. var oDiv = eval_r("document.all.div"+index) 
  34. 当index变量为1时,语句就会被当作varoDiv = document.all.div1 var oDiv = document.all.div1来执行。%{}就是起这个作用。举例: 
  35. <s:set name="myMap"value="#{'key1':'value1','key2':'value2'}"/> 
  36. <s:property value="#myMap['key1']"/> 
  37. <s:url value="#myMap['key1']"/>  
  38. <s:set name="myMap"value="#{'key1':'value1','key2':'value2'}"/> 
  39. <s:property value="#myMap['key1']"/> 
  40. <s:url value="#myMap['key1']"/> 
  41. 上面的代码第2行会在页面上输出“value1”,而第3行则会输出"#myMap['key1']"这么一个字符串。如果将第3行改写成这样:  
  42. <s:url value="%{#myMap['key1']}"/> 
  43. <s:url value="%{#myMap['key1']}"/> 
  44. 则输出为“value1”。   
  45. 这说明struts2里不同的标签对ognl的表达式的理解是不一样的。如果当有的标签“看不懂”类似“#myMap['key1']”的语句时,就要用%{}来把这括进去,“翻译”一下了。 
  46. 3、“$”有两种用途 
  47. (1)、在国际化资源文件中,引用OGNL表达式。 
  48. (2)、在Struts2配置文件中,引用OGNL表达式 
  49. <action name="saveUser"class="userAction"method="save"
  50. <result type="redirect">listUser.action?msg=${msg}</result> 
  51. </action> 
  52. <action name="saveUser"class="userAction"method="save" 
  53. <result type="redirect">listUser.action?msg=${msg}</result> 
  54. </action>

------------------------------------------------------------------------------

--------------------------------------------------

Struts 2中的表达式语言

要使用struts标签需要导入标签库:<%@taglib prefix="s" uri="/struts-tags"%>

Struts 2支持以下几种表达式语言:

1)OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言,是Struts 2默认的表达式语言

2)JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言;

3)Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python、Ruby和Smarttalk等)的一些起特性;

Action类与JSP页面之间的数据传递

1) 通过HttpServletRequest,HttpSession,ServletContext来传递数据。

    a) Action中传数据:在Action类的请求处理方法中先获取各个作用域对象

    ServletActionContext.getRequest();

    ServletActionContext.getRequest().getSession();

    ServletActionContext.getServletContext();

          然后调用相应的setAttribute(String "键", Object 值);

    b) 在JSP页面中取数据:可以使用EL表达式或代码片段来取出对应作用域中属性值。

    c) 页面中的请求参数传递到Action中时,Action中直接定义对应名称的属性,并提供setter方法即可封装此数据。具体机制为拦截器拦截然后反射向action实例中注值:(1)struts2的拦截器,会根据页面提交到action的参数,用放反射技术在action中寻找同名的属性,然后调用action的set方法注入属性值 。(2)如果在action中的属性太多,可以包装成一个对象如person对象,根据表单元素的名称,person.**去寻找action中person属性,如果person为空,
用反射机制new出person对象 ,然后注入person的属性值 , person类必须要有默认的构造函数

   

2) 通过ActionContext实例来传递数据。 ActionContext是Action执行时的上下文,上下文可以看作是一个容器(其实我们这里的容器就是一个Map而已),它存放放的是Action在执行时需要用到的对象,比如:在使用WebWork时,我们的上下文放有请求的参数(Parameter)、会话(Session)、Servlet上下文(ServletContext)、本地化(Locale)信息等。
在每次执行Action之前都会创建新的ActionContext,ActionContext是线程安全的,也就是说在同一个线程里ActionContext里的属性是唯一的,这样我的Action就可以在多线程中使用。

   a) Action中通过ActionContext传递数据。

      ActionContext提供了put(String "键", Object 值);  //数据不会映射到HttpServletRequest中。

      ActionContext提供的getSession().put(String "键", Object 值);  //数据会自动映射到HttpSession中。

      ActionContext提供的getApplication().put(String "键", Object 值); //数据会自动映射到ServletContext中。

   b) 在JSP页面取数据:struts2推荐使用OGNL来取ActionContext中的数据。

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值