OGNL、ValueStack、重构资费列表、Action基本原理

<span style="font-family: 微软雅黑, 'Times New Roman'; font-size: 24px; line-height: 24px; background-color: rgb(255, 255, 255);">1. OGNL</span>

1.1. OGNL介绍

1.1.1. 什么是OGNL

Object Graph Navigation Language,是一门功能强大的表达式语言,类似于EL。

1.1.2. 为什么用OGNL

OGNL表达式功能很强大,后面我们会重点阐述。而Struts2默认采用OGNL表达式访问Action的数据,实际上是通过ValueStack用封装后的OGNL来访问的Action。

1.1.3. OGNL原理

OGNL是独立的开源组件,Struts2对其进行了改造及封装,要想了解Struts2中OGNL的运行原理,需参考ValueStack。

1.2. OGNL用法

1.2.1. Struts2显示标签

Struts2中,OGNL表达式要结合Struts2标签来访问数据,即OGNL表达式要写在Struts2标签内,因此我们先来介绍第一个Struts2的标签——显示标签。

1、语法

<s:property value="OGNL"/>

2、解释

该标签的作用是根据OGNL表达式访问Action,并将取出的数据替换标签本身。如下图,实际上该标签类似于EL表达式中的${ }。

图-1

1.2.2. 2个常用的OGNL表达式

OGNL表达式一共有8种使用方法,其中前2种要求大家必须掌握,是经常要用到的方式,后6种了解即可,下面我们来介绍这2种必须掌握的OGNL。

1、访问基本属性

  • 语法:<s:property value="属性名"/>
  • 解释:这种方式的作用是将Action中的基本属性直接显示到标签的位置,属性名指的是Action中的属性。
  • 举例:<s:property value="name"/>

2、访问实体对象

  • 语法:<s:property value="对象名.属性名"/>
  • 解释:这种方式的作用是将Action中的实体对象属性显示到标签的位置,对象名指的是Action中的实体对象,属性名指的是实体对象中的属性。
  • 举例“<s:property value="user.userName"/>

可以看出,这两种访问Action的方式实际上与EL表达式用法完全一致,是最常用的,也是最容易掌握的2种方式。

1.2.3. 6个需要了解的OGNL表达式

  • 语法:<s:property value="属性名[index]"/>
  • 解释:这种方式的作用是访问Action中的数组或集合的某元素,其中属性指的是Action中的数组或集合属性,index指的是数组或集合的下标。
  • 举例:<s:property value="langs[1]"/>
  • 语法:<s:property value="属性名.KEY"/>
  • 解释:这种方式的作用是访问Action中的Map类型的属性值,其中属性指的是Action中的Map类型属性,KEY指的是Map属性的key,即可以通过其key来直接输出对应的值。
  • 举例:<s:property value="msgMap.success"/>
  • 语法:<s:property value="OGNL和运算"/>
  • 解释:这种方式的作用是在访问Action数据后,可以对OGNL表达式返回的结果直接进行计算,并将计算结果输出于标签位置。
  • 举例:<s:property value="name+‘,你好‘"/>
  • 语法:<s:property value="OGNL.方法"/>
  • 解释:这种方式的作用是在访问Action数据后,可以直接调用OGNL表达式返回值的方法,并将最终的结果输出于标签位置。
  • 举例:<s:property value="name.toUpperCase()"/>
  • 语法:<s:property value="OGNL"/>
  • 解释:这种方式的作用是不访问Action的数据,而是直接使用OGNL表达式创建一个临时的集合,并返回输出于标签位置。
  • 举例:<s:property value="{1,2,3}"/>
  • 语法:<s:property value="OGNL"/>
  • 解释:这种方式的作用是不访问Action的数据,而是直接使用OGNL表达式创建一个临时的Map,并返回输出于标签位置
  • 举例:<s:property value="#{‘bj‘:’北京‘,’sh’:’上海’}"/>

2. ValueStack

2.1. ValueStack介绍

2.1.1. 什么是ValueStack

ValueStack是Struts2中,Action向页面传递数据的媒介,ValueStack封装了Action的数据,并允许JSP通过OGNL来对其进行访问。

2.1.2. ValueStack原理

如下图,Struts2使用了ValueStack对OGNL组件进行了封装,其封装的实际上是改造后的OGNL,当然我们不关注改造的过程,只需要掌握改造后的OGNL在ValueStack中的运行原理即可。

ValueStack中首先封装了OGNL解析引擎,用于解析传入的OGNL表达式,其目的就是在页面上以标签+字符串的方式访问Java对象,从而降低了页面代码的开发难度,提升了页面代码的维护效率,而OGNL表达式传入引擎的时机我们在后面的Action基本原理中会讲到。

OGNL引擎可以访问2种类型的对象,一种是栈类型,另一种是Map类型。

1、栈

  • 默认情况下,OGNL表达式访问的是这个栈,访问的规则是从栈顶向下依次以栈的每一级元素作为root对象来取值,直到取到值则返回,如果没有取到任何数据则返回null。
  • root对象就是JavaBean,只是在ValueStack中按照这种方式称呼而已。OGNL访问root对象,其写法是直接从root对象的属性写起,比如以Action为root对象,访问Action中的user属性,那么OGNL表达式为user.userName。
  • 栈中封装的是最常用的数据,通常情况下是Action和另外一个Struts2自身要使用的对象,其中Action位于栈顶。此类型的目的是为了简化OGNL表达式,这一点我们在后面的迭代标签时会有更直观的体会。

2、Map

  • 如果OGNL表达式以“#”开头,那么OGNL引擎会访问Map类型的对象,此时OGNL表达式的写法为#key,返回的值为该key在Map中对应的值,这种对象通常我们称之为context对象。
  • context对象用于封装程序的上下文数据,包含request、session、page、application等。实际上,context对象封装的是完整的数据,也包含了action的数据。
  • context对象是完整的数据对象,有了它其实就足够了,但是Struts2设计出了栈,完全是处于简化OGNL表达式的目的,当然这种行为会增加一些理解的难度,我们需要重点关注栈里数据的变化,但这和使用的方便相比就不足挂齿了。

总体来说,由于context对象中的数据固定了,因此对context对象的访问比较简单和直接。而栈中数据是会有所变化的,并且访问时也是自顶向下动态访问的,因此我们在学习ValueStack时要重点关注栈的结构和变化。

图-2

2.2. 访问ValueStack

2.2.1. 1、通过debug标签观察其结构

ValueStack的原理是比较抽象的,对于其结构的理解也不够直观。对于这种情况,Struts2提供了一个调试标签,可以用于观察ValueStack的结构,该标签的效果如下图。

图-3

可以看出,该标签会在页面上生成一个debug链接,点击后会展开ValueStack的结构描述内容,其中包括栈的结构及数据、context对象的结构及数据。

该标签的语法比较简单,即<s:debug/>。其作用仅仅是用于调试的,是给开发人员来使用的,当项目提交测试以及上线时,要删除该标记。

值得注意的是,该标签存在互斥性,在页面上如果写多个调试标签,实际上只有第一个是准确的,其他的标签内容有问题。但鉴于该标签仅仅是给开发人员调试使用的,因此我们在了解这个规则的前提下,只要保持页面上只有一个调试标签即可正常使用。

2.2.2. 2、输出栈顶

前面我们介绍了Struts2的显示标签,即<s:property value="OGNL"/>。这个标签有一种特殊的用法,可以直接输出栈顶的内容,语法为<s:property />。

也就是说,将显示标签的value属性去掉,那么该标签将默认输出栈顶的内容。

2.2.3. 3、访问context对象

Context对象是一个Map类型的对象,我们使用OGNL访问它的方式是固定的,即#key,返回的值是当前key在Map中对应的值。

2.2.4. 4、迭代集合

我们可以使用Struts2的迭代标签结合着OGNL,来迭代Action中的集合属性,迭代集合标签的语法如下:

<s:iterator value="users">
	<s:property value="userName"/>
</s:iterator>

users是OGNL表达式,自顶向下访问ValueStack栈中root对象的users属性,这里会从栈顶的Action对象取到该集合属性(List<User> users)值 。

需要注意的是,在迭代的过程中,ValueStack的栈顶会发生变化,循环变量User会被压入栈顶,此时Action被压到栈的第二位,即栈顶由由原来的Action变为循环变量User,如下图

图-4

userName是OGNL表达式,自顶向下访问ValueStack栈中root对象的userName属性,这里会从栈顶的User对象取到userName属性值。

可以看出,这种动态访问栈中数据的方式,对我们的理解增加了一些难度,但是却可以简化循环内部的OGNL表达式,因为在循环时栈顶即为循环变量,我们以它为root写OGNL不用关注变量的名字了,而这部分代码在项目中是十分频繁出现的,因此牺牲一些理解难度的代价是完全值得的。

2.2.5. 5、按数字迭代

有时候我们也需要按照数字的方式进行迭代,比如资费列表的分页功能。对于这种方式,Struts2也有对应的标签支持,按照数字迭代的标签语法如下:

<s:iterator begin="from" end="to" var="k">
	<s:property value="#k"/>
</s:iterator>

from/to是OGNL表达式,自顶向下访问ValueStack栈中root对象的from/to属性,这里会从栈顶的Action对象取到属性(int from=1, to=3)值。

与迭代集合一样,按数字迭代时,栈顶也会变为循环变量。即在循环过程中,循环变量会被压入栈顶,此时Action被压到栈的第二位,如下图

图-5

需要注意的是,我们不能以数字做root对象,因此无法写OGNL访问栈顶的整数(数字内部没东西了,而访问root需要写其内部属性名)。此时可以声明循环变量k,该声明会将循环变量加入context对象中(如k=2),这样我们就可以写#k这样的OGNL表达式来访问context对象,从而得到循环变量的值。

其实,我们也可以不写OGNL表达式,而是直接输出栈顶的值,即<s:property/>。

ValueStack结构看似复杂,但是需要我们重点关注的无非是栈顶的变化,而此变化也仅仅是在循环时发生,因此记住这唯一的变化情况即可,该情况我总结如下:

1、默认情况下栈顶为Action。

2、循环过程中,栈顶为循环变量。

迭代集合时,循环变量是集合中的对象,通常都是实体对象,即栈顶为实体对象,我们可以以实体对象为root来写OGNL表达式。

按数字迭代时,循环变量是数字,我们不能以数字为实体对象写OGNL,如果需要引用该数字,需要通过var声明变量名,然后以“#变量名”来引用它,这种情况下,我们是从context对象中取出的值。

3、循环结束后,栈顶变回Action。

2.3. Struts2对EL的支持

2.3.1. EL表达式如何访问ValueStack

Struts2将数据封装于ValueStack,默认使用OGNL表达式来取值,而在此之前我们却使用了EL表达式取Action的值,那么EL表达式是从哪里取的值,如何取的值呢?

实际上EL表达式也是从ValueStack中取到的值,因为Struts2把传递的数据都放于ValueStack中了。但我们知道EL表达式的取值范围是page、request、session、application,而我们并没有把数据放入上述任何一个对象中,Struts2实现支持EL表达式的方式是以一个request的包装类来替代request,该包装类是request的子类,并覆写了它的getAttribute方法,在取值方法中先试图从原始request对象中取值,如果没有再从ValueStack中取值。

该request包装类的部分源码如下:

3. ValueStack

3.1. 访问ValueStack

3.1.1. 4、迭代集合

我们可以使用Struts2的迭代标签结合着OGNL,来迭代Action中的集合属性,迭代集合标签的语法如下:

<s:iterator value="users">
	<s:property value="userName"/>
</s:iterator>

users是OGNL表达式,自顶向下访问ValueStack栈中root对象的users属性,这里会从栈顶的Action对象取到该集合属性(List<User> users)值 。

需要注意的是,在迭代的过程中,ValueStack的栈顶会发生变化,循环变量User会被压入栈顶,此时Action被压到栈的第二位,即栈顶由由原来的Action变为循环变量User,如下图

图-6

userName是OGNL表达式,自顶向下访问ValueStack栈中root对象的userName属性,这里会从栈顶的User对象取到userName属性值。

可以看出,这种动态访问栈中数据的方式,对我们的理解增加了一些难度,但是却可以简化循环内部的OGNL表达式,因为在循环时栈顶即为循环变量,我们以它为root写OGNL不用关注变量的名字了,而这部分代码在项目中是十分频繁出现的,因此牺牲一些理解难度的代价是完全值得的。

3.1.2. 5、按数字迭代

有时候我们也需要按照数字的方式进行迭代,比如资费列表的分页功能。对于这种方式,Struts2也有对应的标签支持,按照数字迭代的标签语法如下:

<s:iterator begin="from" end="to" var="k">
	<s:property value="#k"/>
</s:iterator>

from/to是OGNL表达式,自顶向下访问ValueStack栈中root对象的from/to属性,这里会从栈顶的Action对象取到属性(int from=1, to=3)值。

与迭代集合一样,按数字迭代时,栈顶也会变为循环变量。即在循环过程中,循环变量会被压入栈顶,此时Action被压到栈的第二位,如下图

图-7

需要注意的是,我们不能以数字做root对象,因此无法写OGNL访问栈顶的整数(数字内部没东西了,而访问root需要写其内部属性名)。此时可以声明循环变量k,该声明会将循环变量加入context对象中(如k=2),这样我们就可以写#k这样的OGNL表达式来访问context对象,从而得到循环变量的值。

其实,我们也可以不写OGNL表达式,而是直接输出栈顶的值,即<s:property/>。

ValueStack结构看似复杂,但是需要我们重点关注的无非是栈顶的变化,而此变化也仅仅是在循环时发生,因此记住这唯一的变化情况即可,该情况我总结如下:

1、默认情况下栈顶为Action。

2、循环过程中,栈顶为循环变量。

迭代集合时,循环变量是集合中的对象,通常都是实体对象,即栈顶为实体对象,我们可以以实体对象为root来写OGNL表达式。

按数字迭代时,循环变量是数字,我们不能以数字为实体对象写OGNL,如果需要引用该数字,需要通过var声明变量名,然后以“#变量名”来引用它,这种情况下,我们是从context对象中取出的值。

3、循环结束后,栈顶变回Action。

3.2. Struts2对EL的支持

3.2.1. EL表达式如何访问ValueStack

Struts2将数据封装于ValueStack,默认使用OGNL表达式来取值,而在此之前我们却使用了EL表达式取Action的值,那么EL表达式是从哪里取的值,如何取的值呢?

实际上EL表达式也是从ValueStack中取到的值,因为Struts2把传递的数据都放于ValueStack中了。但我们知道EL表达式的取值范围是page、request、session、application,而我们并没有把数据放入上述任何一个对象中,Struts2实现支持EL表达式的方式是以一个request的包装类来替代request,该包装类是request的子类,并覆写了它的getAttribute方法,在取值方法中先试图从原始request对象中取值,如果没有再从ValueStack中取值。

该request包装类的部分源码如下:

4. 重构资费列表

4.1. 重构资费列表

4.1.1. 用Struts2标签+OGNL重构资费列表

重构资费列表页面,我们可以分2步进行。

首先重构数据显示区域,将原来的JSTL+EL替换成Struts2标签+OGNL,完成后刷新页面做单元测试。

然后重构分页部分,将原来你的JSTL+EL替换成Struts2标签+OGNL,完成后刷新做单元测试。

5. Action基本原理

5.1. 6大核心组件

5.1.1. 6大核心组件关系

Struts2中包含6大核心组件,它们彼此关联、相互协作,共同处理了一次请求。Struts2理论课实际上就是围绕这6大核心组件展开的,因此我们需要深刻的理解这6大核心组件的关系,这实际上也就是说明了Struts2的基本原理。

如下图,是Struts2中6大核心组件(红色)的关系:

图-8

针对这张图,我们再进一步解释6大核心组件的关系,这里我按照6大核心组件调用的顺序来加以说明。

值得注意的是,每次请求都会重新初始化除了FC之外的其他5大核心组件,即这些组件不是单例的,因此是线程安全的。并且在最终请求结束时销毁这些组件,但在请求未结束之前,这些组件是存活的,因此在JSP的标签中,我们是可以使用OGNL表达式访问ValueStack对象中的数据的。

5.1.2. 6大核心组件作用

1、FC

前端控制器,负责统一的分发请求。

2、Action

业务控制器,负责处理某一类业务。

3、ValueStack

Action与JSP数据交互的媒介。

4、Interceptor

拦截器,负责扩展Action,处理Action的共通事务。

5、Result

负责输出的组件。

6、Tags

标签,负责显示数据、生成框体。

时间: 2024-11-05 14:57:03

OGNL、ValueStack、重构资费列表、Action基本原理的相关文章

OGNL valueStack StackContext(ActionContext)深入分析(转+个人理解)

//还会补充 首先要有一个意识 ,为什么要了解这个?: struts2中的表单是怎么通过表达式(EL or OGNL)来传给Action 和 拿到Action的值的. 值栈(根)对象也可以直接使用EL表达式访问,比如这里可以直接通过${user.username}来获取username的值,我们知道el表达式只能访问四种scope范围内的对象,那为什么这里能访问到值栈对象呢?原因是struts2对HttpServletRequet进行了一次封装,封装的代码主要是重写了getAttribute方法

ActiveAdmin 自定义列表Action

添加自定义按钮 默认情况下,列表中会显示 "查看"."编辑"."删除"按钮.如下. 如何添加一个自定义的按钮呢?如下. 如上图,在列表中添加了"推荐"."取消推荐"的功能. 代码如下. index do selectable_column column "名称",:name column "地址",:address column "创建时间",:c

简易文件夹无限级,使用递归重构层级列表

1 /// <summary> 2 /// 文件夹实体 3 /// </summary> 4 public class Folder 5 { 6 /// <summary> 7 /// 文件夹ID 8 /// </summary> 9 public int id { get; set; } 10 /// <summary> 11 /// 文件夹名称 12 /// </summary> 13 public string name { g

散列表的基本原理与实现

本篇博文主要介绍散列表(Hash Table)这一常见数据结构的原理与实现.由于个人水平有限,文章中难免存在不准确或是不清晰的地方,希望大家可以指正:) 一.概述 符号表是一种用于存储键值对(key-value pair)的数据结构,我们平常经常使用的数组也可以看做是一个特殊的符号表,数组中的“键”即为数组索引,值为相应的数组元素.也就是说,当符号表中所有的键都是较小的整数时,我们可以使用数组来实现符号表,将数组的索引作为键,而索引处的数组元素即为键对应的值,但是这一表示仅限于所有的键都是比较小

OGNL和ValueStack

OGNL简介 OGNL的全称是Object Graph Navigation Language,即对象导航图语言.它是一个开源项目,工作在视图层,其通过简单一致的表达式语言来读取合适者Java对象的属性值,调用对象的方法,遍历整个对象的结构图,实现字段类型的转换. OGNL简介 OGNL的计算都是围绕着OGNL上下文来进行的,OGNL上下文实际就是一个Map集合,由ognl.OgnlContext类来表示(其被封装到ActionContext中).其可以包含一个或多个上下文对象. 先面以一个例子

struts2.1.6教程四、OGNL与ValueStack(VS)

1.值栈入门 下面我们建立struts2ognl项目来练习ognl的使用. 步骤一.搭建strust2的开发环境 步骤二.建立LoginAction,主要代码如下: package com.asm; public class LoginAction extends ActionSupport{ private User user; public String execute() throws Exception { return SUCCESS; } ...省略user的get/set方法 }

struts2(六)之ognl表达式与ActionContext、ValueStack

前言 前面已经把struts2讲内容说了一半了,我写的很详细,希望对博友们有帮助. 一.OGNL表达式语言概述 1.1.OGNL表达式简介 百度上是这样说: OGNL是Object-Graph Navigation Language的缩写,它是一个单独的开源项目.Struts2框架使用OGNL作为默认的表达式语言. 它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的 结构图,实现字段类型转化等功能.它使用相同的表达式去存取对象的属性.

【Struts2】Ognl与ValueStack

一.OGNL 1.1 概述 1.2 OGNL 五大类功能 1.3 演示 二.ValueStack 2.1 概述 2.2 ValueStack结构 2.3 结论 2.3 一些问题 三.OGNL表达式常见使用符号 3.1 #号 3.2 %号 3.3 $号 一.OGNL 1.1 概述 OGNL是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言.比el表达式功能强大.Struts2将ognl表达式语言,集成当sturts2框架中,做为它的默认表达式语言

Struts2的OGNL标签详解

一.Struts2可以将所有标签分成3类: UI标签:主要用于生成HTML元素的标签. 非UI标签:主要用于数据库访问,逻辑控制等标签. Ajax标签:用于Ajax支持的标签. 对于UI标签,则有可以分为两类: 表当标签:表单标签主要用与生成HTML页面的from元素,以及普通表单元素的标签. 非表单标签:非表单标签主要用于生成页面的树.Tab页等. 流程控制标签:主要包含用于实现分页.循环等流程的标签. 数据访问标签:主要包含用于输出ValueStack中的值,完成国际化等功能的标签. Str