4.OGNL(Object Graph Navigation Language),即对象图导航语言

目录


Struts 2专栏目录(点击进入…)



OGNL简介

全称(Object Graph Navigation Language),即对象图导航语言 。它是一个开源项目,工作在视图层,用来取代页面中的Java脚本,简化数据访问的操作(与EL和JSTL类似)。

与JSP 中的内置EL相比,它们同属于表达式语言,用于数据访问,但是OGNL的功能更为强大,提供了EL所不具备的功能;如强大的类型转换功能、访问方法、操作集合对象等。

OGNL是一种强大的技术 ,被集成在Struts 2框架中用来帮助实现数据的传输换和类型转换。OGNL是基于字符串的HTTP输入/输出和基于Java对象的内部处理之间的“粘合剂”,它的功能非常强大。

ONGL在框架中主要做两件事情:①表达式语言和②类型转换器

1.表达式语言

在视图层将数据从页面传入框架和从框架获取输出数据生成页面的过程中,OGNL提供了一个简单的语法,将表单或Struts 2标签与Java各种类型的数据绑定起来
在OGNL的帮助下,输入时,数据从请求参数转移到了Action的JavaBean属性上;输出时,数据又从这些属性转移到生成的页面中

2.类型转换器

处理表达式语言,我们还会用到OGNL类型转换器。每次数据流入和流出框架,页面中字符串类型的数据和Java数据类型之间都会发生转换,我们已经用过一些简单类型的内置转换器


OGNL在框架中的作用

数据的流入

框架在处理每个请求时,都会创建该请求对应的运行环境,并将其请求对应的Action对象放入其中
Action对象被放在一个叫做值栈的(ValueStack)的对象中,并且User对象作为Action对象的一个JavaBean属性而被暴露出来,值栈本身放在运行环境中;params拦截器会负责将来自HttpServletRequest对象的数据传到值栈上

OGNL发挥的作用就是将请求参数的名字映射到值栈的属性上
对于请求参数user.age来说,它会被作为OGNL表达式,针对值栈进行解析。首先看值栈上是否存在user属性,因为值栈会暴露它所报含对象RegistAction(Action)的属性,所以user属性是存在的;其次是看user对象上是否有age属性,下面在调用该属性对应的setter方法,将正确的值赋给user对象;但Request传递进来的都是字符串,所以需要类型转换


数据的流出

数据流出的过程和流入类似,只不过是反过来的。当Action完成业务处理后,最终返回一个结果,并生成页面。最关键的地方在于,在处理请求的过程中,所有的业务数据对象都保留在值栈中;值栈充当了一个容器,通过它,在框架的各个地方可以随时访问这些业务对象。在生成页面的过程中,页面标签可以访问值栈,并通过OGNL表达式获取对象的属性值

ValueStack值栈

值栈就是框架创建的一个存储区域,用来保存Model对象。它具有栈的特征,可以存放多个对象,如果存在多个对象,它们是按照先后顺序压入值栈的,框架在处理每个请求时,都会创建该请求对应的运行环境,这时会创建值栈和请求对应的Action实例,并将Action实例压入值栈中

假如在栈内存放了多个对象,且不同对象存在相同的名字的属性时,那么靠近栈顶的对象优先级更高
值栈是一个虚拟对象,它可以暴露它所包含的所有对象的属性,就好像这些属性是它自己的一样。因为在解析OGNL表达式时,似乎面对的是一个单一对象,但实际并非如此,只是值栈把自己伪装成了一个单一对象。值栈包含存放其中的所有对象的所有属性,假如存放了多个对象,在查找OGNL表达式对应的属性时,会从栈顶开始依次往下查找,一直到栈底,先找到的对象的属性就为“虚拟”对象的属性(OGNL先拿上面的)

元素先进后出,放下一个元素叫压栈,最上面的元素叫栈顶元素

值栈分为两部分:
①root部分,是list结构
②context部分,是Map结构

一般操作的都是root里面的数据
context部分中key的值一般都是固定的,如request,session等,value是其对应的引用。struts 2里面标签 s:debug,使用这个标签可以查看值栈结构和存储值

在Action中没有进行任何操作的时候,栈顶就是Action的引用。Action对象中有值栈对象,值栈对象中有Action的引用

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


类型转换

为什么需要类型转换?

在基于HTTP协议的Web应用中,客户端请求的所有内容(如在表单中输入的姓名、年龄)均以文本编码的方式传输到服务器端,但服务器端的编程语言(如Java)有着丰富的数据类 型,如int、boolean、Date及自定义类型等。

所以为了节省开发时间,提高开发效率。作为一个成熟的MVC框架,Struts 2提供了非常强大的类型转换功能,提供了多种内置类型转换器,自动对客户端传来的数据进行类型转换,这一过程对开发者是完全透明的
还提供了良好的扩展性,如果内置类型转换器不能满足应用需要,开发者还可以简单地开发出自己的类型转换器

内置转换器

转换器种类包括:
(1)String:将int、long、double、boolean、String类型的数组或者java.util.Date类型转换为字符串
(2)boolean/Boolean:在字符串和布尔值之间进行转换
(3)char/Character:在字符串和字符之间进行转换
(4)int/Integer、float/Float、long/Long、double/Double:在字符串和数值数据之间进行转换
(5)Date:字符串和日期类型之间进行转换。具体输入/输出格式与当前的Local相关
(6)数组和集合:在字符串数组和数组对象、集合对象之间进行转换

1.原始类型和包装类

<s:property value=”” default=””/>

property标签用于输出ValueStack中对象属性的值,其中属性用来指定要输出对象的属性,如果没有value属性,则默认输出ValueStack栈顶的对象,类型为Object;
default属性是属性值为空时输出的默认值,类型为String


2.多值类型请求参数的处理

如果在请求中出现多个值对应同一个请求参数的情况,Stuts 2会将这样的数据转换为面向集合的数据类型,如数组、集合。

(1)数组

使用数组处理多值请求非常便利,Struts 2对转移数据到Java数组提供了非常好的支持。

<s:form action=”URI”>
	<s:textfield name=”hobbies” label=”爱好”/>
	<s:textfield name=”hobbies” label=”爱好”/>
	
	<s:textfield name=”numbers[0]” label=”数字”/>
	<s:textfield name=”numbers[1]” label=”数字”/>
	……
</s:form>

第一个数组属性是hobbies,接收爱好信息;
第二个数组属性是numbers,接收数字
如果数据需要转移,这些属性必须存在ValueStack中。通常在Action类中公开这些属性

private String[] hibbies;
private Double[] numbers = new Double[3];
// getter和settter方法

(2)集合

使用List与数据几乎相同,仅有的不同是集合类型可以借助泛型实现

<s:form action=”URI”>
	<s:textfield name=”users.name” label=”姓名1:”/>
	<s:textfield name=”users.name” label=”姓名2:”/>
	<s:submit value=”提交”/>
</s:form>

相同的名称参数有多个数据,可以将具有相同名称的参数看作一个集合。

特殊说明users.name参数,当OGNL解析这个表达式时,它会首先定位users属性,与其他两个不同的是,users是一个指定了元素为User对象的List。

private List<User> users;
// getter和settter方法

List类型的属性看起来很像数组
(1)不需要初始化任何一个List
(2)如果没有类型说明,List类型中的元素都会是String
(3)如果List指定了元素类型为JavaBean,会自动创建指定类型对象作为List的元素


自定义转换器

1. 自定义类型转换器基类:org.apache.struts2.util.StrutsTypeConverter

StrutsTypeConverter类是抽象类,定义两个抽象方法

// 将一个字符串直接转换为指定的
public Object convertFromString(Map context,String[] values,Class toType)
方法参数描述
context OGNL上下文的Map对象
values要转换的字符串值
toType要转换的目标类型
// 将指定对象转化为字符串
public String convertToString(Map context,Object object)
方法参数描述
contextOGNL上下文的Map对象
object要转换的对象

2. 配置自定义类转换器

自定义类型转换器后,还必须配置,将类型转换器和某个类或属性通过properties文件建立关联。

Struts 2提供了两种方式来配置转换器,①一种是应用于全局范围的类型转换器,②一种是应用于特定类的类型转换器

(1)应用于全局范围的转换器
要指定应用于全局范围的类型转换器,需要在classpath的根路径下(通常是WEB-INF/classes目录,对应用开发时的src目录)创建一个名为xwork-conversion.properties的属性文件,其内容为转换类全名=类型转换器类全名

(2)应用于特定类的类型转换器
要指定应用于特定类的类型转换器,需要在特定类的相同目录下创建一个名为ClassName-conversion.properties的属性文件(ClassName代表实际的类名)其内容为特定类的属性名=类型转换器类全名

3. 创建和配置日期类型转换器

在src目录下创建文件xwork-conversion.properties,并添加代码java.util.Date=类的全限定类名。


处理类型转换错误

1.前提条件

(1)启动StrutsConversionErrorInterceptor拦截器:该拦截器已经包含了在defaultStack拦截器栈中,如果在struts.xml中扩展了struts-default包,启动项目时会自动加载
(2)实现ValidationAware接口,ActionSupport实现了该接口
(3)配置input结果映射。出现类型转换错误后将在所匹配页面显示错误信息,如果没有配置将出现错误提示,提示没有指定input页面
(4)在页面使用Struts 2表单标签或使用<s:fielderror>标签来输出转换错误。Struts 2表单标签内嵌了除数错误信息的功能

2.修改所有类型的转换错误的信息

在默认情况下,所有的类型转换错误都是通用的l18N消息键xwork.default.invalid.fieldvalue来报告错误信息的,默认文本是“Invalid field value for fieldxxx”,xxx是字段名称。如果希望提高友好型,可修改默认的错误信息文本

<constant name=”struts.custom.il8n.resources” value=”message” />

然后在src目录下创建资源文件message.properties,并添加文本:

xwork.default.invalid.fieldvalue=字段”{0}”的值无效

3.定制特定的字段的类型转换错误信息

I18N消息键xwork.default.invaild.fieldvalue的设置对图中所有的类型转换错误都适用

如果希望字段单独定制转换错误信息,则可以在Action范围的资源文件中添加I18N消息键invalid.fieldvalue.xxx,其中xxx是字段名称
Invalid.fieldvalue.timeDate=日期转换错误


OGNL表达式

OGNL 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.utils.Map 的接口。
Struts框架默认就支持Ognl表达式语言。(从struts项目必须引入ognl.jar包可以看出)

OGNL 取根元素不用#号,取非根元素要使用#号

OgnlContext对象在源码中实现了Map接口:

public class OgnlContext implements Map {……}

OGNL 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了 java.utils.Map 的接口。

1.访问bean属性

表达式是由属性链构成的,如user.father.father.name这个表达式由4个链式的属性构成,它引用了user祖父的名字;既可以用对表示来给属性赋值,也可以用它来获取属性的值,这取决于使用的场景。

<s:form action=”Resister”>
	<s:textfield name=”user.address.streeName” label=”请输入街道名称”/>
	<s:submit value=”提交”/>
</s:form>

框架会用这个表达式在值栈中定位属性,并将其设置为用户输入的街道名称。需要注意的是,在属性链中间的某个属性对象是null的情况下,框架会自动创建对象,并赋值给该属性。

该操作两个前提:
①对象类型必须是遵循JavaBean规范的类,即这个类必须具有无参数的构造方法,否则无法自动创建实例
②属性必须提供setter方法,否则框架无法为该属性赋值

获得属性的值:

// 街道名称
<s:property value=”user.address.streeName”/>

2.访问集合对象

// 迭代数组:
<s:iterator value=”hobbies”>
	<s:property/>
</s:iterator>

// 迭代集合(元素类型为Double)
<s:iterator value=”numbers”>
	<s:property/>
</s:iterator>

// 迭代集合(元素类型为User)
<s:iterator value=”users” id=”user”>
	<s:property value=”name”/>
	<s:property value=”age”/>
	<s:property value=”#user.address”/>
</s:iterator>

访问ActionContext中的数据
提供了<s:debug />标签,该标签的作用是辅助测试,该标签会在页面中生成一个超链接,单击超链接会分别显示ValueStack和Stack Context中的所有信息


Srtuts 2标签和OGNL表达式

在标签中使用表达式需要注意点

(1)Struts 2标签的属性都可使用OGNL表达式,Struts 2标签的属性是具有类型的,分为字符串类型和对象类型
例如:创建URL的标签<s:url>中的value属性为字符串类型
<s:set>、<s:property>标签的value属性为Object类型

(2)对于字符串类型的属性,如果要访问动态数据,必须使用%{……}这样的语法,否则将被直接看作字符串常量

(3)对于对象类型的属性,将直接作为OGNL表达式进行计算。如果需要对对象类型的属性指定字符串常量,则必须在这个字符串常量外加上一对单引号或者使用%{‘’}

(4)如果对对象类型的属性使用了%{……}语法,则语法会被汇率,而直接把内容当作OGNL表达式求解。例如:<s:property value=”%{#myurl}”/>和<s:property value=”#myurl” />作用相同。如果分不清一个属性的值的类型是不是字符串类型,则可以直接加上${}


URL标签和日期标签

URL标签

该标签借助param子元素可以指定在跳转URL的同时传递参数。

<s:url value=”url”>
	<s:param name=”parname” value=”parvalue”/>
</s:url>

语法:
在<s:url/>标签中,value属性表示指定生成URL地址;而<s:param/>标签是一个参数标签,表示需要传递的参数信息,其中,name属性表示传递的参数名称,value属性表示传递参数所具有的的值。


日期标签

日期标签用于格式化输出一个日期,该标签可以计算指定日期和当前日期时刻之间的时差

语法:

<s:date format=”format” nice=”true|false” name=”name” id=”id” />
属性描述
format表示按照指定的格式进行日期格式化
nice该属性只有true和false两个值,用于指定是否输出指定日期与当前时间的时差,默认是flase
name表示当前需要格式化的日期
id表示引用该元素的id值

数据存入值栈的三种方式

1.使用值栈对象中的set方法(继承ActionSupport)

// 1.使用ActionContext类来获取值栈对象
ActionContext context = ActionContext.getContext();
// 2.获取值栈对象
ValueStack vs = context.getValueStack();
vs.set("键", "值");
// set方法是将数据存到新创建的map集合中

2.使用值栈中的push方法(继承ActionSupport)

// 1.使用ActionContext类来获取值栈对象
ActionContext context = ActionContext.getContext();
// 2.获取值栈对象
ValueStack vs = context.getValueStack();
vs.push("字符串");
// 在值栈中创建了字符串,然后将数据存入

3.使用自定义变量的方式加上get方法(继承ActionSupport)

// 1.定义变量
private List<User> list = new ArrayList<User>();

// 2.生成get方法
public List<User> getList() {
	return list;
}

public String execute() {
	// 3.向list中存数据
	User user =new User()
	user.setUsername("admin");
	user.setPassword("123456");
	list.add(user);
}
// 不需要重新开辟空间,因此该种方法在开发中较为常用

获取值栈中的数据

1.set保存的数据

 username:<s:property value="one" />
 password:<s:property value="top" />

2.获取值栈里面的对象

username:<s:property value="user.username" />
password:<s:property value="user.password" />

3.JSTL获取集合

<c:forEach items="${list }" var="user">
	username:${user.username }
	password:${user.password }
</c:forEach>

4.通过OGNL取值,request、seesion、applcation等

<s:property value="#seesion.ww" />
<s:property value="#request.ww" />
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值