众所周知,Strut 2的Action类通过属性可以获得所有相关的值,如请求参数、Action配置参数、向其他Action传递属性值(通过chain结果)等等。要获得这些参数值,我们要做的唯一一件事就是在Action类中声明与参数同名的属性,在Struts 2调用Action类的Action方法(默认是execute方法)之前,就会为相应的Action属性赋值。
要完成这个功能,有很大程度上,Struts 2要依赖于ValueStack对象。这个对象贯穿整个Action的生命周期(每个Action类的对象实例会拥有一个ValueStack对象)。当Struts 2接收到一个.action的请求后,会先建立Action类的对象实例,但并不会调用Action方法,而是先将Action类的相应属性放到ValueStack对象的顶层节点(ValueStack对象相当于一个栈)。只是所有的属性值都是默认的值,如String类型的属性值为null,int类型的属性值为0等。
在处理完上述工作后,Struts 2就会调用拦截器链中的拦截器,当调用完所有的拦截器后,最后会调用Action类的Action方法,在调用Action方法之前,会将ValueStack对象顶层节点中的属性值赋给Action类中相应的属性。大家要注意,在这里就给我们带来了很大的灵活性。也就是说,在Struts 2调用拦截器的过程中,可以改变ValueStack对象中属性的值,当改变某个属性值后,Action类的相应属性值就会变成在拦截器中最后改变该属性的这个值。
从上面的描述很容易知道,在Struts 2的的Action类可以获得与属性同名的参数值就是通过不同的拦截器来处理的,如获得请求参数的拦截器是params,获得Action的配置参数的拦截器是staticParams等。在这些拦截器内部读取相应的值,并更新ValueStack对象顶层节点的相应属性的值。而ValueStack对象就象一个传送带,将属性值从一个拦截器传到了另一个拦截器(当然,在这其间,属性值可能改变),最后会传到Action对象,并将ValueStack对象中的属性的值终值赋给Action类的相应属性
拦截器的源代码:packageinterceptors;
importjava.util.Enumeration;
importjava.util.Map;
importjava.util.Properties;
importjava.io.InputStream;
importjava.io.FileInputStream;
importcom.opensymphony.xwork2.ActionContext;
importcom.opensymphony.xwork2.ActionInvocation;
importcom.opensymphony.xwork2.config.entities.ActionConfig;
importcom.opensymphony.xwork2.interceptor.AbstractInterceptor;
importcom.opensymphony.xwork2.util.ValueStack;
publicclassPropertyInterceptorextendsAbstractInterceptor
{
privatestaticfinalStringDEFAULT_PATH_KEY="path";
privatestaticfinalStringDEFAULT_ENCODING_KEY="encoding";
privatestaticfinalStringDEFAULT_SEPARATOR_KEY="separator";
protectedStringpathKey=DEFAULT_PATH_KEY;
protectedStringencodingKey=DEFAULT_ENCODING_KEY;
protectedStringseparatorKey=DEFAULT_SEPARATOR_KEY;
publicvoidsetPathKey(StringpathKey)
{
this.pathKey=pathKey;
}
publicvoidsetEncodingKey(StringencodingKey)
{
this.encodingKey=encodingKey;
}
publicvoidsetSeparatorKey(StringseparatorKey)
{
this.separatorKey=separatorKey;
}
@Override
publicStringintercept(ActionInvocationinvocation)throwsException
{
ActionConfigconfig=invocation.getProxy().getConfig();
Map<String,String>parameters=config.getParams();
if(parameters.containsKey(pathKey))
{
Stringpath=parameters.get(pathKey);
Stringencoding=parameters.get(encodingKey);
Stringseparator=parameters.get(separatorKey);
if(encoding==null)
encoding="UTF-8";
if(separator==null)
separator="";
path=invocation.getAction().getClass().getResource(path)
.getPath();
Propertiesproperties=newProperties();
InputStreamis=newFileInputStream(path);
java.io.Readerreader=newjava.io.InputStreamReader(is,encoding);
properties.load(reader);
ActionContextac=invocation.getInvocationContext();
ValueStackstack=ac.getValueStack();
System.out.println(stack.hashCode());
Enumerationnames=properties.propertyNames();
while(names.hasMoreElements())
{
// 下面会使用setValue方法修改ValueStack对象中的相应属性值
Stringname=names.nextElement().toString();
if(!name.contains("."))
stack.setValue(name,properties.get(name));
StringnewName=null;
newName=parameters.get(name.replaceAll(".",""));
if(newName!=null)
stack.setValue(newName,properties.get(name));
if(!separator.equals(""))
{
newName=name.replaceAll(".","");
stack.setValue(newName,properties.get(name));
}
newName=name.replaceAll(".",separator);
stack.setValue(newName,properties.get(name));
}
}
returninvocation.invoke();
}
}
ognl标签用法:
访问属性
名字属性获取 :<s:property value="user.username"/><br> 地址属性获取 :<s:property value="user.address.addr"/><br> |
访问方法
调用值栈中对象的普通方法: <s:property value="user.get()"/><br> |
访问静态属性和方法
调用 Action 中的静态方法: <s:property value="@struts.action.LoginAction@get()"/> 调用 JDK 中的类的静态方法: <s:property value="@java.lang.Math@floor(44.56)"/><br> 调用 JDK 中的类的静态方法 ( 同上 ) : <s:property value="@@floor(44.56)"/><br> 调用 JDK 中的类的静态方法: <s:property value="@java.util.Calendar@getInstance()"/><br> 调用普通类中的静态属性: <s:property value="@struts.vo.Address@TIPS"/><br> |
访问构造方法
调用普通类的构造方法 :<s:property value="new struts.vo.Student(' 王老吉 ' , ' 爱国饮料 ' , 3 , 99).username"/>
|
1.5. 访问数组
获取 List:<s:property value="testList"/><br> 获取 List 中的某一个元素 ( 可以使用类似于数组中的下标获取 List 中的内容 ): <s:property value="testList[0]"/><br> 获取 Set:<s:property value="testSet"/><br> 获取 Set 中的某一个元素 (Set 由于没有顺序,所以不能使用下标获取数据 ): <s:property value="testSet[0]"/><br> × 获取 Map:<s:property value="testMap"/><br> 获取 Map 中所有的键 :<s:property value="testMap.keys"/><br> 获取 Map 中所有的值 :<s:property value="testMap.values"/><br> 获取 Map 中的某一个元素 ( 可以使用类似于数组中的下标获取 List 中的内容 ): <s:property value="testMap['m1']"/><br> 获取 List 的大小 :<s:property value="testSet.size"/><br> |
访问集合 – 投影、选择 (? ^ $)
利用选择获取 List 中成绩及格的对象 :<s:property value="stus.{?#this.grade>=60}"/><br> 利用选择获取 List 中成绩及格的对象的 username: <s:property value="stus.{?#this.grade>=60}.{username}"/><br> 利用选择获取 List 中成绩及格的第一个对象的 username: <s:property value="stus.{?#this.grade>=60}.{username}[0]"/><br> 利用选择获取 List 中成绩及格的第一个对象的 username: <s:property value="stus.{^#this.grade>=60}.{username}"/><br> 利用选择获取 List 中成绩及格的最后一个对象的 username: <s:property value="stus.{$#this.grade>=60}.{username}"/><br> 利用选择获取 List 中成绩及格的第一个对象然后求大小 : <s:property value="stus.{^#this.grade>=600}.{username}.size"/><br> |
集合的伪属性
OGNL 能够引用集合的一些特殊的属性 , 这些属性并不是 JavaBeans 模式 , 例如 size(),length() 等等 . 当表达式引用这些属性时 ,OGNL 会调用相应的方法 , 这就是伪属性 .
集合 | 伪属性 |
Collection(inherited by Map, List & Set) | size ,isEmpty |
List | iterator |
Map | keys , values |
Set | iterator |
Iterator | next , hasNext |
Enumeration | next , hasNext , nextElement , hasMoreElements |
Lambda :[…]
格式: :[…]
使用 Lambda 表达式计算阶乘 : <s:property value="#f = :[#this==1?1:#this*#f(#this-1)] , #f(4)"/><br> |
OGNL 中 # 的使用
# 可以取出堆栈上下文中的存放的对象 .
名称 | 作用 | 例子 |
parameters | 包含当前 HTTP 请求参数的 Map | #parameters.id[0] 作用相当于 request.getParameter("id") |
request | 包含当前 HttpServletRequest 的属性( attribute) 的 Map | #request.userName 相当于 request.getAttribute("userName") |
session | 包含当前 HttpSession 的属性( attribute )的 Map | #session.userName 相当于 session.getAttribute("userName") |
application | 包含当前应用的 ServletContext 的属性( attribute )的 Map | #application.userName 相当于 application.getAttribute("userName") |
attr | 用于按 request > session > application 顺序访问其属性( attribute ) |
|
获取 Paraments 对象的属性: <s:property value="#parameters.username"/>
OGNL 中 % 的使用
用 %{} 可以取出存在值堆栈中的 Action 对象 , 直接调用它的方法 .
例如你的 Action 如果继承了 ActionSupport . 那么在页面标签中,用 %{getText('key')} 的方式可以拿出国际化信息 .
OGNL 中 $ 的使用
“ $ ”有两个主要的用途
l 用于在国际化资源文件中,引用 OGNL 表达式
l 在 Struts 2 配置文件中,引用 OGNL 表达式
值栈
ValueStack 对象。这个对象贯穿整个 Action 的生命周期(每个 Action 类的对象实例会拥有一个 ValueStack 对象)。当 Struts 2 接收到一个 .action 的请求后,会先建立 Action 类的对象实例,但并不会调用 Action 方法,而是先将 Action 类的相应属性放到 ValueStack 对象的顶层节点( ValueStack 对象相当于一个栈)。
在 Action 中获得 ValueStack 对象: ActionContext.getContext().getValueStack()
l Top 语法
使用 Top 获取值栈中的第二个对象 :<s:property value="[1].top. 对象 "/>
l N 语法
使用 N 获取值栈中的第二个对象 :<s:property value="[1]. 对象 "/>
l @ 语法
调用 action 中的静态方法: <s:property value="@vs1@ 静态方法 "/> vs :值栈 1 :表示第一个。