OGNL全称是Object Graph Navigation Lanaguage, 即对象图形导航语言。工作在视图层,用来取代页面中的java脚本。简化数据访问操作。法,和jsp中内置的EL表达相比,它们都是表达式语言,但是OGNL的功能更强大,可以进行类型转换,访问方法,操作集合对象等。
OGNL主要做两件事情,
1. 表达式语言,数据在框架中的流入流出。OGNL提供了一个简单的语法 ,将表单或者Struts2标签与各ava各种类型的数据绑定起来。如<input type="text" name="user.name">的输入对应Action类中的User对象的name属性。在OGNL的帮助下,输入的时候。数据从请求参数转移到了Action的JavaBean属性上;输出时,数据又从属性转移到生成的页面中。
2. 类型转换器
每一次数据流入或流出框架,页面中字符串类型的数据和Java数据类型之间都会发生转换。OGNL可以实现和各种Java数据类型间的转换,包括集合类型。
框架在处理每个请求时,都会创建该请求对应的运行环境,并将请求对应的Action对象放入其中。Action对象被放在一个叫作值栈的对象中,Action对象的属性被暴露出来。params拦截器会负责把来自HttpServletRequeat对象的数据传到值 栈上。
ValueStack值栈:
值栈是框架创建的一个存储区域,用来保存Model对象。它具有栈的特征,可以存放多个对象,如果存放多个对象,它们是按照先后顺序压入值栈的。框架在处理每个请求时,都会创建该请求对应的运行环境,这时会创建值栈和请求对应的Action实例,并将Action实例压入值栈中。
值栈是一个虚拟对象,它可以暴露它所包含的所有对象的属性,就好像这些属性是它自己的一样。为什么说值栈是一个虚拟对象呢?因为在解析OGNL表达式时,我们好像面对的是一个单一对象,但实际上并不是这样的,只是值栈把自己伪装成了一个单一对象。值栈包含存放的所有对象的所有属性,假如存放了多个对象,在查找OGNL表达式对应的属性时,会从栈顶开始依次往下查找,一直到栈底,先找到的对象的属性就作为"虚拟"对象的属性。换句话说,假如栈内存放了多个对象,且不同对象存在相同名字的属性时,那么靠近栈顶的对象的优先级更高,下面对象的该属性就被"隐藏"了。
struts2内置转换器:
对于大部分常用类型,并不需要我们自己编码实现类型转换,struts2框架内部提供的类型转换器可以帮我们完成。这些类型转换器有很多种,用于实现将字符串类型和常用类型之间的转换。具体的转换器种类包括 :
① String : 将int,long,double,String类型的数组或者java.util.Date类型转换为字符串。
② boolean/Boolean: 在字符串和布尔值之间进行转换。
③ char/Character: 在字符串和字符之间进行转换。
④ int/Integer,float,/Float, long/Long, double/Double: 在字符串和数值类型之间进行转换。
⑤ Date : 在字符串和日期类型之间进行转换。
⑥ 数组集合: 在字符串数组和数组对象,集合对象之间进行转换。
自定义转换器:
创建自定义类型转换器使用StrutsTypeConvert抽象类,它定义了两个抽象方法,用于不同的转换方法,分别如下:
public Object convertFormString(Map context,String[]values,Class toType) : 将一个或多个字符串值转换为指定的类型。context表示OGNL上下文的Map对象,参数values是要转换的字符串值,参数toType是要转换的目标类型。
public String converToString(Map context,Object object) : 将指定对象转化为字符串。context表示OGNL上下文的Map对象,参数Object是要转换的对象。
如果继承StrutsTypeConverter类编写自定义类型,需要重写上面的两个抽象方法。
自定义类型转换器后,还必须进行配置,将类型转换器和某个类或属性通过properties文件建立关联。Struts2提供了两种方式来配置转换器,一种是应用全局范围的类型转换器,一种是应用于特定类的类型转换器。
① 应用于全局范围的类型转换器
要指定应用于全局范围的类型转换器,需要在classpath的根路径下(通常是WEB-INF/classess目录,对应开发的src目录,) 创建一个名为xwork-conversion.properties的属性文件,其内容为 :转换类全名=类型转换器类全名
② 应用于特定类的类型转换器
要指定于特定类的类型转换器,需要在特定类的相同目录下创建一个名为ClassName-conversion.properties的属性文件(ClassName代表实际的类名),其内容为 : 特定类的属性名=类型转换器类全名
下面代码创建和配置一个日期类型转换器
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import com.opensymphony.xwork2.conversion.TypeConversionException;
public class DateConverter extends StrutsTypeConverter{
//支持转换的多种日期格式,可增加时间格式
private final DateFormat[]dfs={
new SimpleDateFormat("yyyy年MM月dd日"),
new SimpleDateFormat("yyyy-MM-dd"),
new SimpleDateFormat("MM/dd/yy"),
new SimpleDateFormat("yyyy.MM.dd"),
new SimpleDateFormat("yyMMdd"),
new SimpleDateFormat("yyyy/MM/dd") };
/**
* 将指定格式字符串转换为日期类型
*/
@Override
public Object convertFromString(Map context, String[] values, Class toType) {
String dateStr=values[0]; //获取日期的字符串
for (int i = 0; i < dfs.length; i++) {
try {
return dfs[i].parse(dateStr);
} catch (Exception e) {
continue;
}
}
//如果遍历完毕后仍没有转换成功,表明出现转换异常
throw new TypeConversionException();
}
/**
* 将日期转换为指定格式字符串
*/
@Override
public String convertToString(Map context, Object object) {
Date date=(Date)object;
//输出的格式是yyyy-MM-dd
return new SimpleDateFormat("yyyy-MM-dd").format(date);
}
}
然后在src目录下创建文件xwork-conversion.properties,并添加如下代码 :
java.util.Date=包名.DateConverter
如果在页面输入了错误格式的内容,除了在页面中使用JavaScript进行判断外,也可以在服务器端判断。如果要在服务器端判断类型转换错误,需要满足如下前提条件。
① 启动StrutsConversionErrorInteceptor拦截器。这个拦截器已经包含在defaultStack拦截器栈中,参看struts-default.xml文件。如果在struts.xml中扩展了"struts-default"包,启动项目时会自动加载。
② 实现validationAware接口,ActionSupport实现了该口。
③ 配置input结果映射。出现类型转换错误后将在所配置页面显示错误信息,如果没有配置将出现错误提示,提示没有指定 input页面。
④ 在页面使用Struts2表单标签或使用<s:fielderror>标签来输出转换错误。
在默认情况下,所有的类型转换错误都是通用的i18N消息键xwork.default.invalid.fieldvalue来报告错误信息的,默认文本是
"Invalid field value for fieldxxx",xx是字段名称 。希望提高友好性,可修改默认的错误信息文本。如下面代码所示:
<constant name="struts.custom.i18n.resource value="message" />
然后在src目录下创建资源文件message.properties,并添加文本,如下代码所示。
xwork.default.invalid.fieldvalue=字段"{0}"的值无效
i18n消息键xwork.default.invalid.fieldvalue的设置对图中所有的类型转换错误都适用。如果希望为特定字段单独定制转换错误信息,则可以在Action范围的资源文件中添加i18n消息键invalid.fieldvalue.xxx,其中xxx是字段。
invalid.fieldvalue.timeDate=日期转换错误
OGNL作为表达式语言,功能十分强大,通常使用它来引用各种Java对象的属性。
1. 访问Bean属性
表达式是由属性链构成的,如user.father.father.name,这个表达式由4个链式的属性构成,它引用了user祖父的名字。
我们即可以用表达式来给属性赋值,也可以用它来获取属性的值。在属性链中间的某个对象属性是null的情况下,框架会自动创建对象,并赋值给该属性。它有个两个前提,第一,对象类型必须是遵循JavaBean规范的类,即这个类必须具有无参数的构造方法,否则无法自动创建实例,第二,属性必须提供setter方法,否则框架无法为该属性赋值。
2. 访问集合对象
我们用一段代码来说明如何访问集合对象
Actiono类代码:
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
public class ShowArrayAndListAction extends ActionSupport{
private String[]hobbies;
private List<Double>numbers;
private List<User>users;
@Override
public String execute(){
//省略代码
return SUCCESS;
}
}
在页面展示数据:
<%-迭代数组-%>
<s:iterator values="hobbies">
<s:property/>
</s:iterator>
<%-迭代集合 元素类型为Double-%>
<s:iterator values="numbers">
<s:property/>
</s:iterator>
<%-迭代集合 元素类型为User-%>
<s:iterator values="users">
用户名: <s:property value="name"/></br>
年龄: <s:property value="age"/></br>
</s:iterator>
iterator标签迭代一个集合或者数组,其中value属性用于指定要迭代的集合属性,类型可以是Collection,Map,Iterator或者数组类型。iterator标签在迭代过程中,会把迭代的每一个对象暂时压入栈顶,这样在该标签内部可以直接访问元素对象的属性和方法,就可以使用不带value属性的property标签输出数据。在iterator标签执行完毕后,位于栈项的对象就会被删除。迭代下一个元素时,重复该过程。
访问列表或数据的某个元素,可以用属性名[index]的方式,如users[1].name。
访问Map的某一个元素,可以用属性名[key]的方式 ,如userMap['userA'].name。
可以通过size或者length来访问集合的长度。
访问ActionContext中的数据:
Actioncontext中所包含的内容:
① ValueStack ② parameters ③ application ④ session ⑤ attr ⑥ request
OGNL所有表达式的解析必须针对ActionContext中的某个对象,这个对象也称为根对象。默认情况下,OGNL表达式是针对值栈解析 的,值栈是默认的根对象,也可以通过明确指定名字的方式将其他对象作为根对象。下面对ActionContext中的对象进行说明 。
application:用于访问application属性。#application.username或者#application['username'],相当于调用 application.getAttribute("username")。
session: 用于访问sessin属性,#session.username或者 #session["username"],相当于session.getAttribute("username")。
request :用于访问request属性,#request.username 或者 #request["username"],相当于request..getAttribute("username").。
parameters :用于访问HTTP的请求参数。#parameters.username或者 #parameters['username']相当于调用request.getParameterValues("username"),将返回一个数组。
attr : 按照pageContext——request——session——application顺序访问其属性。
查看ActionContext中的数据:
在Struts2中为了方便页面的数据调试和查看,特别提供了<s:debug>标签,该标签的作用就是辅助调试,该标签会在页面中生成一个超链接,单击超链接会分别显示ValueStack和 Stack Context中的所有信息。
Struts2标签与OGNL表达式:
使用OGNL表达式最多的地方就是Struts2标签,在标签中使用表达式需要注意以下几点。
① Struts2标签的 属性都可以使用OGNL表达式。Struts2标签的属性是具有类型的,分为字符串类型和对象类型。例如创建URL 的标签<s:url>中的value属性为字符串类型,<s:set><s:property>标签的value属性为Object类型。
② 对于字符串类型的属性,如果要访问动态数据,必须使用%{...}这样的语法,否则将被直接看作字符常量。例如:
<s:set name="myurl" value=" 'http://www.sohu.com' "/>
<s:url value="#myurl"/> //显示#myurl
<s:url value="%{#myurl}"/> 显示http://www.sohu.com
③ 对于对象类型的属性,将直接作为OGNL表达式进行计算。如果需要对对象类型的属性指定字符常量,则必须在这个字符串 常量外加上一对单引号或者使用%{'constant string'}这样的语法 。
//要使用单引号或%{' '}
<s:set name="myurl" value=" 'http://www.sohu.com' "/>
<s:url value="#myurl"/> // 显示http://www.sohu.com
④ 如果对对象类型的属性使用了%{..}的语法,则语法会被忽略,而直接把内容当作OGNL表达工求解。例如,
<s:property value="%{#myurl}" /> 和 <s:property value="#myrul" /> 作用相同。如果分不清一个属性的值的类型是不是字符串类型,则可以直接加上%{...} 。
URl标签:
URL标签的作用是构建一个URL地址,在该标签中借助param子元素可以指定在跳转URL的同时传递的参数
语法 :<s:url value="url"
<s:param name="parname" value="parvalue" />
</s:url>
URL的标签使用如下:
<body>
使用value指定url地址
<s:url value="http://www.sohu.com"/><br/>
使用变量生成url地址
<s:set name="myurl" value="http://www.sohu.com"/>
<s:url value="%{#myurl}"/><br/>
使用param指定参数<br/>
<s:url value="show.action">
<s:param name="id" value="123" />
</s:url>
</body>
日期标签:
日期标签用于格式化输出一个日期,除此之外,还可以计算指定日期和当前日期时刻之间的时差。
语法 : <s:date format="format" nice="true | false" name="name" id="id" />
format属性:表示按照指定的格式进行日期格式化。
nice属性: 用于指定是否输出指定日期与当前时间的时差,只有true和false两个值 ,默认是false 。
name属性: 表示当前需要格式化的日期。
id属性:表示引用该元素的id值。
我们通过一段代码来了解<s:date./>标签的用法
public class DateAction extends ActionSupport{
private Date currentDate;
public String execute(){
currentDate=new Date();
return SUCCESS;
}
public Date getCurrentDate(){
return currentDate;
}
public void setCurrentDate( Date currentDate){
this.currentDate=currentDate;
}
}
页面:
<body>
指定日期格式
<s:date name="currentDate" format="dd/MM/yyyy" /><br/>
不指定日期格式
<s:date name="currentDate"/>
</body>