Struts2-值栈与OGNL

OGNL表示式使用 和 值栈
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
* xwork 提供 OGNL表达式
* ognl-3.0.5.jar
OGNL 是一种比EL 强大很多倍的语言

OGNL 提供五大类功能
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问
3、访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )
4、支持赋值操作和表达式串联
5、操作集合对象。

1、 使用OGNL访问 对象方法 和 静态方法
* OGNL 在jsp 结合 struts2 标签库 使用 , <s:property value="ognl表达式" /> 执行 ognl表达式
调用 实例方法 : 对象.方法() ---- <s:property value="'hello,world'.length()"/>
调用 静态方法 : @[类全名(包括包路径)]@[方法名] --- <s:property value="@java.lang.String@format('您好,%s','小明')"/>
* 使用 静态方法调用 必须 设置 struts.ognl.allowStaticMethodAccess=true

2、 访问OGNL上下文(OGNL context)和ActionContext
OGNL上下文(OGNL context) 对象 ----- 值栈 ValueStack

问题一 : 什么是值栈 ValueStack ?
ValueStack 是 struts2 提供一个接口,实现类 OgnlValueStack ---- 值栈对象 (OGNL是从值栈中获取数据的 )
每个Action实例都有一个ValueStack对象 (一个请求 对应 一个ValueStack对象 )
在其中保存当前Action 对象和其他相关对象 (值栈中 是有Action 引用的 )
Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中,request中 (值栈对象 是 request一个属性)

问题二 : 值栈的内部结构 ?
值栈由两部分组成
ObjectStack: Struts 把动作和相关对象压入 ObjectStack 中--List
ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中
Struts 会把下面这些映射压入 ContextMap 中
parameters: 该 Map 中包含当前请求的请求参数
request: 该 Map 中包含当前 request 对象中的所有属性
session: 该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前 application 对象中的所有属性
attr: 该 Map 按如下顺序来检索某个属性: request, session, application

ValueStack中 存在root属性 (CompoundRoot) 、 context 属性 (OgnlContext )
* CompoundRoot 就是ArrayList
* OgnlContext 就是 Map
context 对应Map 引入 root对象
* context中还存在 request、 session、application、 attr、 parameters 对象引用
* OGNL表达式,访问root中数据时 不需要 #, 访问 request、 session、application、 attr、 parameters 对象数据 必须写 #
* 操作值栈 默认指 操作 root 元素

问题三 : 值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系 ?
值栈对象 是请求时 创建的
doFilter中 prepare.createActionContext(request, response);
* 创建ActionContext 对象过程中,创建 值栈对象ValueStack
* ActionContext对象 对 ValueStack对象 有引用的 (在程序中 通过 ActionContext 获得 值栈对象 )
Dispatcher类 serviceAction 方法中 将值栈对象保存到 request范围
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

问题四 : 如何获得值栈对象
获得值栈对象 有两种方法
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
ValueStack valueStack2 = ActionContext.getContext().getValueStack();

问题五: 向值栈保存数据 (主要针对 root)
两种方式
// 将数据保存root的索引0位置,放置到第一个元素 ArrayList add(0,element);
valueStack.push("itcast");

    // 在值栈创建参数map, 将数据保存到map中
    valueStack.set("company", "传智播客");
    valueStack.set("pany","itcast");
    
在jsp中 通过 <s:debug /> 查看值栈的内容 

问题六: 在JSP中获取值栈的数据
访问root中数据 不需要#
访问 其它对象数据 加 #

通过下标获取root中对象
<s:property value="[0].top"/> //取值栈顶对象
直接在root中查找对象属性 (自上而下自动查找)
valueStack:<s:property value="username"/>

在OgnlContext中获取数据
request:<s:property value="#request.username"/>
session:<s:property value="#session.username"/>
application:<s:property value="#application.username"/>
attr:<s:property value="#attr.username"/>
parameters:<s:property value="#parameters.cid[0]"/>

================================== valueStack对象 保存在request范围, 值栈生命周期 就是 request的生命周期 ,值栈的内部结构(root、ognlContext) 、获取值栈对象、向值栈保存数据、在JSP显示值栈的内容

3、 值栈在开发中应用
主流应用 : 值栈 解决 Action 向 JSP 传递 数据问题

Action 向JSP 传递数据处理结果 ,结果数据有两种形式
1) 消息 String类型数据
this.addFieldError("msg", "字段错误信息");
this.addActionError("Action全局错误信息");
this.addActionMessage("Action的消息信息");

  • fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)、 actionMessage 通用消息

在jsp中使用 struts2提供标签 显示消息信息
<s:fielderror fieldName="msg"/>
<s:actionerror/>
<s:actionmessage/>

2) 数据 (复杂类型数据)
使用值栈 valueStack.push(products);

哪些数据默认会放入到值栈 ???
1)每次请求,访问Action对象 会被压入值栈 ------- DefaultActionInvocation 的 init方法 stack.push(action);
* Action如果想传递数据给 JSP,只要将数据保存到成员变量,并且提供get方法就可以了
2)ModelDriven 接口 有一个单独拦截器
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
在拦截器中 ,将model对象 压入了 值栈 stack.push(model);
* 如果Action 实现ModelDriven接口,值栈默认栈顶对象 就是model对象

4、 值栈的数据 通过EL访问
问题七:为什么 EL也能访问值栈中的数据

StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(request);
* 对Request对象进行了包装 ,StrutsRequestWrapper
* 重写request的 getAttribute
Object attribute = super.getAttribute(s);
if (attribute == null) {
attribute = stack.findValue(s);
}
访问request范围的数据时,如果数据找不到,去值栈中找
* request对象 具备访问值栈数据的能力 (查找root的数据)

============================================================================================================================
5、 OGNL表达式 常见使用

、 % 、$ 符号使用

1) # 的 使用
用法一 # 代表 ActionContext.getContext() 上下文
<s:property value="#request.name" /> ------------> ActionContext().getContext().getRequest().get("name");

request

session

application

attr

parameters

用法二 : 不写# 默认在 值栈中root中进行查找
<s:property value="name" /> 在root中查找name属性

  • 查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素 <s:property value="[1].name" /> 访问栈中第二个元素name属性
  • 访问第二个元素对象 <s:property value="[1].top" />

用法三 :进行投影映射 (结合复杂对象遍历 )
1)集合的投影(只输出部分属性
<h1>遍历集合只要name属性</h1>
<s:iterator value="products.{name}" var="pname">
<s:property value="#pname"/>
</s:iterator>
2)遍历时,对数据设置条件
<h1>遍历集合只要price大于1500商品</h1>
<s:iterator value="products.{?#this.price>1500}" var="product">
<s:property value="#product.name"/> --- <s:property value="#product.price"/>
</s:iterator>
3)综合
<h1>只显示价格大于1500 商品名称</h1>
<s:iterator value="products.{?#this.price>1500}.{name}" var="pname">
<s:property value="#pname"/>
</s:iterator>

用法四: 使用#构造map集合
经常结合 struts2 标签用来生成 select、checkbox、radio
<h1>使用#构造map集合 遍历</h1>
<s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry">
key : <s:property value="#entry.key"/> , value: <s:property value="#entry.value"/>

</s:iterator>

  1. %的使用
    用法一: 结合struts2 表单表单使用, 通过%通知struts, %{}中内容是一个OGNL表达式,进行解析
    <s:textfield name="username" value="%{#request.username}"/>
    用法二: 设置ognl表达式不解析 %{'ognl表达式'}
    <s:property value="%{'#request.username'}"/>

3)$的使用
用法一 :用于在国际化资源文件中,引用OGNL表达式
在properties文件 msg=欢迎您, ${#request.username}
在页面
<s:i18n name="messages">
<s:text name="msg"></s:text>
</s:i18n>
* 自动将值栈的username 结合国际化配置信息显示
用法二 :在Struts 2配置文件中,引用OGNL表达式

<param name="contentType">${contentType}</param>
* ${contentType} 读取值栈中contentType数据,在Action提供 getContentType 因为Action对象会被压入值栈,
contentType是Action属性,从值栈获得

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352

推荐阅读更多精彩内容