ValueStack是一个接口,在struts2中使用OGNL(Object-Graph Navigation Language)表达式实际上是使用 实现了ValueStack接口的类OgnlValueStack.它是ValueStack的默认实现类.
ValueStack贯穿整个action的生命周期,每一个action实例都拥有一个ValueStack对象,其中保存了当前action对象和其他相关对象.
struts2把ValueStack对象保存在名为:struts.valueStack的request域中.即ValueStack作用域为request.当action创建的时候,ValueStack就创建了,action被销毁的时候,ValueStack就销毁了
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。
Struts2使用OGNL作为默认的表达式语言。
相对于EL表达式,它提供了平时我们需要的一些功能,如:
支持对象方法调用,支持各类静态方法调用和值访问,支持操作集合对象。
一、ValueStack
1.ValueStack是一个接口,在struts2中使用OGNL(Object-Graph Navigation Language)表达式实际上是使用
实现了ValueStack接口的类OgnlValueStack.它是ValueStack的默认实现类.
2.ValueStack贯穿整个action的生命周期,每一个action实例都拥有一个ValueStack对象,其中保存了当前action对象和其他相关对象.
3.struts2把ValueStack对象保存在名为:struts.valueStack的request域中.即ValueStack作用域为request.当action创建的时候,ValueStack就创建了,action被销毁的时候,ValueStack就销毁了
4.ValueStack中的数据分两部分存放:root(栈结构,CompoundRoot)和context(map形式,OgnlContext)
其中的root对象是CompoundRoot,CompoundRoot继承了ArrayList,提供了额外的方法:push(),和pop()方法,
用来对root对象中所包含的数据进行存取.正是由于这两个方法,CompoundRoot变成了一个栈结构.struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack
的CompoundRoot中),所以action对象是CompoundRoot中的一个元素.其中的context对象是OgnlContext,它实现了map接口,在valuestack的默认实现类中,即OgnlValueStack类中,
调用ongl中的方法:Ognl.createDefaultContext(..)给context赋值,查看源代码可知,
此方法返回的是一个OgnlContext对象.
5.获取ValueStack的三种方式
(1)
ValueStack valueStack = ActionContext.getContext().getValueStack();
ActionContext为Action的上下文,其中有重要的6大对象
- valueStack,parameters,request,session,application,attr
- 而ognl表达式必须选择AcitonContext中的一个对象作为根对象(root),默认为valueStack,因此访问valueStack中的数据不加#而如果要使用另外5个对象作为根对象,需要加#前缀.
(2)
ValueStack valueStack = ServletActionContext.getValueStack(ServletActionContext.getRequest());
ServletActionContext继承了ActionContext,它与web容器耦合,可以获取web容器的一些信息.
(3)
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
7.常用的存储数据的方法
(1)向map栈中存数据,即ValueStack中的context(OgnlContext)
-
通过request,session等向map中存储数据
ServletActionContext.getRequest().setAttribute("username","joey"); ServletActionContext.getSession().put("pwd","123");
-
直接向map栈中放数据
ActionContext.getContext().put("msg","how you doing");
(2)向对象栈中存数据,即ValueStack中的root(CompoundRoot)对象
先得到root,再把数据压入到root中,这种方式是放入到栈底.
ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.getRoot().add(new Person());
先得到root,利用add(index,Object)把一个对象压入到root中指定位置.
ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.getRoot().add(0,new Person());//这里0,表示压入栈顶.
存放一个map到对象栈中
ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.set("msg","dude"); //先封装成Map,在把Map放入到对象栈中,且放入栈顶.
利用valueStack的push方法把一个对象直接压入栈顶
ValueStack valueStack = ActionContext.getContext().getValueStack(); valueStack.push(new Person());
8.利用OGNL表达式取ValueStack中的数据
(1)<s:property />:取栈顶的元素.
(2)<s:iterator />:取栈顶的元素.
注意:此标签的value属性值无论来自对象栈还是Map栈,都可以不加#前缀(<s:select/>标签也适用).
因为此标签在迭代的时候,总是把当前正在迭代的元素放入到栈顶.
二、ognl
1.
ognl是struts2中使用的一种表达式语言,可用于jsp的标签库中,以方便访问各种对象的属性.
它用于把界面的参数传递到Action中(并进行类型转换),它还可以用于struts2的配置文件中.
2.Root对象
OGNL称为对象图导航语言,所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象.
例如:
public class User{
private String username;
private Group group;
//省略setter,getter方法
}
public class Group{
private String name;
private Organization org;
//省略setter,getter方法
}
public class Organization{
private String orgId;
//省略setter,getter方法
}
以上三个类,描述了通过一个User对象,同过User对象可以导航到Group对象,进而导航到Organization对象.
以User为根,通过基本的getters方法,可以访问到此根对象的其它关联对象.【对象图的导航,必须通过getters方法导航】
注意:
导航的目的,就是为了获取某个对象的值或设置某个对象的值或调用某个对象的方法.
OGNL表达式语言的真正目的,是为了在那些不能写java代码的地方执行java代码,或者是为了更方便的执行java代码.
3.Context对象
在OGNL表达式中,有可能需要访问到多个毫不相干的对象,这时候,我们需要给OGNL传递一个Map类型的对象,把表达式中需要用到的对象放到Map中即可.而这个Map对象称为Context
要在表达式访问到context中的对象,需要使用"#对象名称"的语法规则.
存在context的主要原因就是在OGNL中,不支持多个root对象,而如果需要在表达式中访问多个毫不相关的对象时,
只能通过一个Map来把这些对象统一传递给OGNL.
4.普通方法访问
首先在User中增加一个成员方法,代码如下:
public String get(){
return "这是User中的get方法";
}
在LoginAction中也有类似的get方法,随后再在loginSuc.jsp中增加如下代码:
<s:property value="user.username.length()"/><br> //调用值栈对象中的普通方法1
<s:property value="user.get()"/><br> //调用值栈对象中的普通方法2
<s:property value="get()"/><br> //调用LoginAction中的普通方法
4.静态方法访问
在LoginAction中增加如下方法:
public static String getSta() {
return "这是LoginAction中的静态方法";
}
然后在loginSuc.jsp中增加如下代码:
<s:property value="@com.asm.LoginAction@getSta()"/><br> //调用Action中的静态方法
<s:property value="@vs@getSta()"/><br> //调用LoginAction中的静态方_方式(2)
说明:我们在方式二中用到@vs,只有那些值栈中的对象才可以这样写。
然后访问,发现访问不到,因为在struts2.1.6的版本中,struts.ognl.allowStaticMethodAccess的默认值为false,我们只需在struts.xml中增加如下内容:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
再来访问时便可以访问到。
5.默认类Math的访问
在loginSuc.jsp中增加如下代码:
<s:property value="@java.lang.Math@min(1,2)"/><br>//调用Math类中的静态方法
<s:property value="@@min(1,2)"/><br>//调用Math类中的静态方法_方式2
<s:property value="@@PI"/><br>//调用Math类中的字段
说明:因为是默认的类,所以可以省略类名
6.调用普通类的构造方法
调用普通类中的构造方法 :
<s:property value="new com.asm.vo.Student('jack','20','85.5')"/><br>
调用普通类中的构造方法并访问其字段 :
<s:property value="new com.asm.vo.Student('jack','20','85.5').name"/>
说明:第一种是只new出对象,显示的时候其实是调用对象的toString方法。
7.集合对象初步
首先在LoginAction中增加如下字段并提供相应的get/set方法:
private List myList = new ArrayList();
private Set mySet = new HashSet();
private Map myMap = new HashMap();
然后再在execute方法中初始化这些集合对象,代码如下:
myList.add("list1");
myList.add("list2");
myList.add("list3");
myList.add("list4");
mySet.add("set1");
mySet.add("set3");
mySet.add("set1");
mySet.add("set2");
myMap.put("m1", "map1");
myMap.put("m3", "map3");
myMap.put("m2", "map2");
最后在loginSuc.jsp中增加如下代码:
<s:property value="myList"/><br>//获取List
<s:property value="myList[0]"/><br>//获取List中的第一个元素
<s:property value="mySet"/><br>//获取Set
<s:property value="mySet[0]"/><br>//获取Set中的第一个元素(set无序,不能取到)
<s:property value="myMap"/><br>//获取Map
<br>
获取Map中的key=m1的元素的值:
<s:property value="myMap.m1"/>//方式一
<s:property value="myMap['m1']"/><br><hr>//方式二
获取List的大小:
<s:property value="myList.size"/>|<s:property value="myList.size()"/><br>
<s:property value="myMap.keys"/><br>//获取Map中所有键
<s:property value="myMap.values"/><br>//获取Map中所有值
8.集合对象进阶
首先在LoginAction中增加如下字段并提供相应的get/set方法:
private List studentList = new ArrayList();
然后再在execute中为其初始化赋值,代码如下:
studentList.add(new Student("jack", 20, 86.0f));
studentList.add(new Student("lily", 22, 96.5f));
studentList.add(new Student("tom", 23, 56.5f));
最后在loginSuc.jsp中增加如下代码:
<s:property value="studentList"/><br>//获取List中的Student对象
<s:property value="studentList.{name}"/><br>//利用投影获取List中的name属性
<s:property value="studentList.{age}"/><br>//利用投影获取List中的age属性
<s:property value="studentList.[0]{name}"/>
<s:property value="studentList.{name}[0]"/><br>//利用投影获取List中的第一个对象的name属性
利用选择获取List中grade>60的student信息:
<s:property value="studentList.{?#this.grade>60}"/><br>
利用选择获取List中grade>60的student名字信息:
<s:property value="studentList.{?#this.grade>60}.{name}"/><br>
利用选择获取List中grade>60的第一个student名字信息:
<s:property value="studentList.{?#this.grade>60}.{name}[0]"/><br>
利用选择获取List中grade>60的第一个student名字信息(链表):
<s:property value="studentList.{^#this.grade>60}.{name}"/><br>
利用选择获取List中grade>60的最后一个student名字信息(链表):
<s:property value="studentList.{$#this.grade>60}.{name}"/><br>
说明:这里重点是说明?#的使用,结合此例来看,studentList中有许多Stutdent对象,我们可以用条件来限制取哪些对象,这些条件必须以?#开始,并且条件要用{}括起。而this是指在判断studentList中的对象是否符合条件的当前对象。?#是指取出符合条件的所有Student对象,而^#是指取出符合条件的第一个对象,$#是指取出符合条件的最后一个对象。
9.N语法top语法
我们在loginSuc.jsp中增加如下下代码:
N语法[0]:<s:property value="[0]"/><br>
N语法[1]:<s:property value="[1]"/><br>
N语法[0].top:<s:property value="[0].top"/><br>
N语法[1].top:<s:property value="[1].top"/><br>
N语法top:<s:property value="top"/><br>
N语法取值:<s:property value="[0].user.username"/><br>
N语法取值:<s:property value="top.user.username"/><br>
说明:规定栈顶的对象为[0],而我们只使用[0]的意思是从值栈中第一个对象取,一直取至栈底。N的意思是从值栈中的第N个对象开始,取到栈底为止。如果要想访问某个对象,需要使用[N].top,它的意思是取出符合N语法的栈顶对象,比如在这里,[0]会取出两个对象,而[0].top是取出这两个对象的栈顶对象。纯top可以简洁地取出值栈中的栈顶对象。
当我们通过chain链访问时,值栈中可能有两个以上的Action对象,如果这些对象中存在相同的属性,N便能正确区分他们。
从上面的N语法取值实例中,我们知道[N]top语法的一个重要作用就是能通过它们引用值栈对象中的属性。结合前面的五种[N]top语法实例,不难理解这里的取值实例。
补充:在此实例中,我们用<s:debug>调试会发现,值栈中还有一个DefaultTextProvider对象(因为此Action继承自ActionSupport),它的作用是获取资源文件中的内容(其实本质是ActionSupport重写了getText()方法),这也就是在国际化问题中我们能直接调用它的getText()方法的原因。
10.获取Stack Context中的信息
我们知道,除了可以从值栈中获取信息,还可以从StackContext中获取信息,只是要加上#,下面我们通过scope对象来演示。
首先是在LoginAction中增加如下字段:
Map myRequest;
Map mySession;
随后再用前面提到的在Action中获取Scope对象”的方式二来完成这些对象的初始化。即实现RequestAware和SessionAware接口。
然后再在execute方法中增加如下内容:
myRequest.put("req", "Req属性");
mySession.put("ses", "Ses属性");
最后在loginSuc.jsp中增加如下代码:
<s:property value="#request.req"/><br>//获取Request属性
<s:property value="#session.ses"/><br>//获取Session属性
<s:property value="#parameters.mes"/>//获取parameters属性
说明:我们获取这些对象都用了#,因为这些对象都是存在一般的ContextMap中,而不是存在值栈中。别最后一个信息的获取是因为我们在login.jsp中增加了如下代码:
<input type="hidden" name="mes" value="the message is transfer by hidden">
关于这些scope的更多信息可以参看下表:
名称 | 作用 | 例子 |
---|---|---|
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顺序访问其属性 | #application.userName相当于application.getAttribute("userName") |
11.总结$ # %的区别
$用于i18n和struts配置文件
取得ActionContext的值
%将原来的文本串解析为ognl,对于本来就是ognl的文本不起作用。形式:%{要解析的文本串}
12.总结OGNL[重点]
OGNL有一个上下文的概念,这个上下文件实质就是一个Map结构,它实现了java.utils.Map接口,在struts2中上下文的实现为ActionContext
当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以接受OGNL访问。
访问上下文中的对象需要使用#号标注命名空间,如#application、#session。另外OGNL会设定一个根对象,在struts2中根对象就是ValueStack值栈对象,如果要访问根对象中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。
在struts2中,根对象的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的变量,就是使用这个List变量来存放一组对象。在root变量(List类型)中处于第一位的对象叫栈顶对象,通常我们在Ognl表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下寻找。
注意:struts2中 ,OGNL表达式需要配合struts的标签才可以使用。