Struts2传值的背后机制:Value Stack(值栈)

1. 数据传输背后机制:ValueStack(值栈)

  在这一切的背后,是因为有了ValueStack(值栈)!

   ValueStack基础:OGNL

  要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)!

OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性;它用于界面将参数传递到Action(并进行类型转换)中;它还可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本机制。

 Root对象

 OGNL称为对象图导航语言。所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。如:

  User实体

 1 package cn.com.leadfar.struts2.actions;
 2
 3 public class User {
 4     private String username;
 5     private Group group;
 6
 7     public String getUsername() {
 8        return username;
 9     }
10     public void setUsername(String username) {
11        this.username = username;
12     }
13     public Group getGroup() {
14        return group;
15     }
16     public void setGroup(Group group) {
17        this.group = group;
18     }
19 }

  Group实体

 1 package cn.com.leadfar.struts2.actions;
 2 public class Group {
 3
 4     private String name;
 5     private Organization org;
 6
 7     public String getName() {
 8        return name;
 9     }
10     public void setName(String name) {
11        this.name = name;
12     }
13     public Organization getOrg() {
14        return org;
15     }
16     public void setOrg(Organization org) {
17        this.org = org;
18     }
19 }

  Orgnization实体

 1 package cn.com.leadfar.struts2.actions;
 2
 3 public class Organization {
 4
 5     private String orgId;
 6
 7     public String getOrgId() {
 8        return orgId;
 9     }
10     public void setOrgId(String orgId) {
11        this.orgId = orgId;
12     }
13 }

上面三个类,描述了通过一个User对象,可以导航到Group对象,进而导航到Organization对象,以User对象为根,一个对象图如下所示:

User(root)

   -- username

   -- group

       -- name

       -- org

           -- orgId

在真实的环境下,这个对象图可能会极其复杂,但是通过基本的getters方法,都应该能够访问到某个对象的其它关联对象。【对象图的导航,必须通过getters方法进行导航】

下述代码将创建一个User对象,及其相关的一系列对象:

1 User user = new User();
2 Group g = new Group();
3 Organization o = new Organization();
4 o.setOrgId("ORGID");
5 g.setOrg(o);
6 user.setGroup(g);

如果通过JAVA代码来进行导航(依赖于getters方法),导航到Organization的orgId属性,如下所示:

1 //用JAVA来导航访问
2 user.getGroup().getOrg().getOrgId();

【注意:导航的目的,是为了获取某个对象的值或设置某个对象的值或调用某个对象的方法!】

【注意:OGNL表达式语言的真正目的,是为了在那些不能写JAVA代码的地方执行JAVA代码,或者是为了更方便地执行JAVA代码】

利用OGNL进行导航的代码如下:

1 //利用OGNL表达式访问
2 String value = (String)Ognl.getValue("group.org.orgId", user);

Ognl.getValue()方法的第一个参数,就是一条OGNL表达式,第二个参数是指定在表达式中需要用到的root对象!

 完整代码如下:

 1    public void testOgnl01() throws Exception{
 2
 3        User user = new User();
 4        user.setUsername("张三");
 5        //利用OGNL表达式访问user对象的username属性
 6        String value = (String)Ognl.getValue("username", user);
 7        log(value);
 8
 9     }
10
11     public void testOgnl02() throws Exception{
12
13        User user = new User();
14        Group g = new Group();
15        Organization o = new Organization();
16        o.setOrgId("ORGID");
17        g.setOrg(o);
18        user.setGroup(g);
19
20        //用JAVA来导航访问
21        log(user.getGroup().getOrg().getOrgId());
22        //利用OGNL表达式访问
23        String value = (String)Ognl.getValue("group.org.orgId", user);
24        log(value);
25     }
26
27     public void testOgnl03() throws Exception{
28
29        User user = new User();
30        Group g = new Group();
31        Organization o = new Organization();
32        o.setOrgId("ORGID");
33        g.setOrg(o);
34        user.setGroup(g);
35
36        //用JAVA来导航访问
37        log(user.getGroup().getOrg().getOrgId());
38
39        //也可以在表达式中使用#root来代表root对象
40        String value = (String)Ognl.getValue("#root.group.org.orgId", user);
41        log(value);
42     }
43
44     private void log(Object o){
45        System.out.println(o);
46     }    

  Context对象

在OGNL的表达式中,有可能需要访问到多个毫不相干的对象,这时候,我们需要给OGNL传递一个Map类型的对象,把表达式中需要用到的对象放到Map中即可!这个Map对象,称为context。要在表达式中访问到context中的对象,需要使用“#对象名称”的语法规则。

如:

 1     public void testOgnl04() throws Exception{
 2
 3        User user = new User();
 4        user.setUsername("张三");
 5        Group g = new Group();
 6        Organization o = new Organization();
 7        o.setOrgId("ORGID");
 8        g.setOrg(o);
 9        user.setGroup(g);
10        User user2 = new User();
11        user2.setUsername("李四");
12        /**
13         * 所谓context其实就是一个Map类型的对象。主要是因为在OGNL中,不支持多个root对象,那么
14         * 如果需要在表达式中访问更多毫不相干的对象时,只能通过一个Map来把这些对象统一传递给OGNL。
15         */
16
17        Map context = new HashMap();
18        context.put("u1", user);
19        context.put("u2", user2);
20
21        //在表达式中需通过“#+对象的名称”来访问context中的对象
22        //如果表达式中没有用到root对象,那么可以用任意一个对象代表root对象!
23        String value = (String)Ognl.getValue("#u1.username + ‘,‘ + #u2.username", context,new Object());
24        log(value);
25     }
26
27     public void testOgnl05() throws Exception{
28        User user = new User();
29        user.setUsername("张三");
30        Group g = new Group();
31        Organization o = new Organization();
32        o.setOrgId("ORGID");
33        g.setOrg(o);
34        user.setGroup(g);
35
36        User user2 = new User();
37        user2.setUsername("李四");
38
39        User user3 = new User();
40        user3.setUsername("王五");
41
42        Map context = new HashMap();
43        context.put("u1", user);
44        context.put("u2", user2);
45
46        //给OGNL传递root对象及context对象,以便解释对应的表达式
47        String value = (String)Ognl.getValue("#u1.username + ‘,‘ + #u2.username + ‘,‘ + username", context,user3);
48        log(value);
49     }        

  利用OGNL表达式赋值

OGNL表达式也可以用于赋值操作。

 1     public void testOgnl06() throws Exception{
 2        User user = new User();
 3        //调用setValue()方法来进行赋值
 4        //第一个参数:OGNL表达式
 5        //第二个参数:root对象
 6        //第三个参数:要赋的值
 7        Ognl.setValue("username", user, "张三");
 8        log(user.getUsername());
 9     }
10
11     public void testOgnl07() throws Exception{
12
13        User user = new User();
14        Map context = new HashMap();
15        context.put("u", user);
16
17        //调用setValue()方法来进行赋值
18        //第一个参数:OGNL表达式
19        //第二个参数:context对象
20        //第三个参数:root对象
21        //第四个参数:要赋的值
22        Ognl.setValue("#u.username", context, new Object(), "张三");
23        log(user.getUsername());
24     }
25
26     public void testOgnl08() throws Exception{
27        User user = new User();
28        Map context = new HashMap();
29        context.put("u", user);
30        //利用赋值符号"="来进行赋值
31        Ognl.getValue("#u.username = ‘李四‘", context, new Object());
32        log(user.getUsername());
33     }
34
35     public void testOgnl09() throws Exception{
36
37        User user1 = new User();
38        User user2 = new User();
39
40        Map context = new HashMap();
41        context.put("u1", user1);
42        context.put("u2", user2);
43
44        //在一个表达式中可以用逗号分隔,同时执行多个表达式
45        Ognl.getValue("#u1.username = ‘李四‘,#u2.username=‘王五‘", context, new Object());
46        log(user1.getUsername());
47        log(user2.getUsername());
48     }

利用OGNL调用对象的方法

 1     //************************* OGNL调用对象的方法 *****************************//
 2     public void testOgnl10() throws Exception{
 3
 4        User user = new User();
 5        //如果是调用root对象的方法,可以直接使用方法的名称来调用方法
 6        Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);
 7        log(value);
 8     }
 9
10     public void testOgnl11() throws Exception{
11        User user = new User();
12        user.setUsername("李四");
13        //如果是调用root对象的方法,可以直接使用方法的名称来调用方法
14        String value = (String)Ognl.getValue("getUsername()", user);
15        log(value);
16     }
17
18     public void testOgnl12() throws Exception{
19        User user = new User();
20        Ognl.getValue("setUsername(‘王五‘)", user);
21        String value = (String)Ognl.getValue("getUsername()", user);
22        log(value);
23     }
24     //************************* OGNL调用静态方法和变量 *********************//
25     public void testOgnl13() throws Exception{
26
27        User user = new User();
28        user.setUsername("王五");
29        //调用静态变量
30        //注意:out是System中的静态变量,out是PrintStream类型的一个对象
31        //而println()则是out这个对象中的实例方法(不是静态方法)
32        //调用静态方法,需要在类名和变量名前面加上@来调用,对于实例方法,用"."来调用
33        Ognl.getValue("@[email protected](username)", user);
34     }
35
36     public void testOgnl14() throws Exception{
37
38        User user = new User();
39        user.setUsername("wangwu");
40        //调用静态方法,注意使用全路径类名  Ognl.getValue("@[email protected](@[email protected](username))", user);
41
42     }

利用OGNL访问集合,数组

 1 public void testOgnl15() throws Exception{
 2
 3        Object root = new Object();
 4        Map context = new HashMap();
 5
 6        //利用OGNL创建java.util.List对象
 7        List list = (List)Ognl.getValue("{123,‘xxx‘,‘kdjfk‘}", context, root);
 8        context.put("list", list);
 9
10        //利用OGNL创建数组
11        int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);
12        context.put("intarray", intarray);
13
14        //利用OGNL表达式创建java.util.Map对象
15        Map mapvalue = (Map)Ognl.getValue("#{‘listvalue‘:#list,‘intvalue‘:#intarray}", context, root);
16        context.put("mapvalue", mapvalue);
17
18        //利用OGNL表达式访问这些数组和集合对象
19        Ognl.getValue("@[email protected](#list[1])", context,root);
20        Ognl.getValue("@[email protected](#intarray[2])", context,root);
21        Ognl.getValue("@[email protected](#mapvalue.listvalue[0])", context,root);
22        Ognl.getValue("@[email protected](#mapvalue[‘intvalue‘][0])", context,root);
23
24     }
25
26     public void testOgnl16() throws Exception{
27
28        List root = new ArrayList();
29
30        User user1 = new User();
31        user1.setUsername("张三");
32
33        User user2 = new User();
34        user2.setUsername("李四");
35
36        root.add(user1);
37        root.add(user2);
38
39        //如果root对象是List类型
40        log(Ognl.getValue("#root[0].username", root));
41        log(Ognl.getValue("#root[1].username", root));
42     }

应用ValueStack

理解ValueStack的基本机制!对各种现象作出解释。ValueStack实际上就是对OGNL的封装,OGNL主要的功能就是赋值与取值,Struts2正是通过ValueStack来进行赋值与取值的!

ValueStack是一个接口,而OgnlValueStack是strtus2中的缺省实现。ValueStack中的数据,分两个部分存放:root和context(这与OGNL中的概念一致),同时ValueStack暴露相关的接口:

1 void setValue(String expr, Object value);
2 Object findValue(String expr);

用来通过OGNL表达式对ValueStack中的数据进行操作! ValueStack中的root对象是CompoundRoot,CompoundRoot继承了ArraryList,提供了额外的方法:push()和pop()方法,用来对root对象中所包含的数据进行存取!

 1 public class CompoundRoot extends ArrayList {
 2     public CompoundRoot() {
 3     }
 4     public CompoundRoot(List list) {
 5         super(list);
 6     }
 7     public CompoundRoot cutStack(int index) {
 8         return new CompoundRoot(subList(index, size()));
 9     }
10     public Object peek() {
11         return get(0);
12     }
13     public Object pop() {
14         return remove(0);
15     }
16     public void push(Object o) {
17         add(0, o);
18     }
19 }

正是通过这两个方法,CompoundRoot变成了一个栈结构!压栈操作,将导致对象被放到CompoundRoot的第0个元素上(第0个元素是栈顶),其它对象被依次往后移动;出栈操作,将导致CompoundRoot的第0个元素被移除(即栈顶元素被弹出),其它对象被依次往前移动!

OGNL不支持多个root对象,而struts2能够支持多个root对象,它对OGNL做了扩展。

如果某个OGNL表达式被传递给ValueStack(即调用ValueStack的setValue或findValue方法),而表达式中包含有对root对象的访问操作,ValueStack将依次从栈顶往栈底搜索CompoundRoot对象中所包含的对象,看哪个对象具有相应的属性,找到之后,立刻返回。

在Struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是CompoundRoot中的一个元素。看下面的代码:

 1 public class UserAction {
 2
 3     private String username;
 4     private Integer age;
 5     private boolean valid;
 6
 7     //查看用户的详细信息
 8     public String detail(){
 9        username = "张三";
10        age = 18;
11        valid = true;
12        return "detail";
13     }

在Action中,给Action的username/age/valid赋值。Detail页面如下:

1 username:<s:property value="username"/> <br/>
2 valid:<s:property value="valid"/> <br/>
3 age:<s:property value="age"/> <br/>

  上述JSP页面将能正确将它们的值取出。<s:property value=”ognl表达式”/>。在s:property标签中的OGNL表达式,最终会交给ValueStack来解释。username就是一个OGNL表达式,意思是调用root对象的getUsername()方法。Struts2将自动搜索CompoundRoot中有哪些元素(从第0个元素开始搜索),检测这些元素是否有getUsername()方法,如果第0个元素没有getUsername()方法,将继续搜索第1、2、3……个元素是否有getUsername()方法。

在上面的例子中,CompoundRoot中只有一个对象,就是userAction对象,而这个对象中正好有getUsername()方法,所以,上述JSP代码将能够将值正确取出, 再看下面的例子:

 1 public class UserAction {
 2     private String username;
 3     private String name;
 4     //查看用户的详细信息
 5     public String detail(){
 6        username = "张三";
 7        name = "王五";
 8        User u = new User();
 9        u.setUsername("赵毅");
10        ActionContext.getContext().getValueStack().push(u);
11        return "detail";
12     }

在上面这个UserAction的代码中,我们直接调用ActionContext.getContext().getValueStack().push()方法,把一个User对象(这个对象拥有getUsername()和setUsername()方法)直接压入到ValueStack中,这时候,在ValueStack的CompoundRoot中将有两个元素:第0个元素是刚刚压入的user对象[赵毅],而第1个元素是userAction对象[张三],如果在JSP中使用下面的表达式来取值:

<s:property value=”username”/> ,那么输出的值将是“赵毅”!道理上面已经讲过了,struts2将会从第0个元素开始搜索CompoundRoot中的对象,第0个元素正是刚刚压入的那个user对象!

如果在JSP中使用<s:property value=”name”/>来取值,将取出“王五”,因为第0个元素user对象没有name属性,所以,会继续搜索第1个元素userAction对象,在这个对象中就有name属性了!

再看下面的代码:

 1 public class UserAction {
 2     private String username;
 3     //查看用户的详细信息
 4     public String detail(){
 5        username = "张三";
 6        List list = new ArrayList();
 7
 8        for(int i=0; i<10; i++){
 9            User user = new User();
10            user.setUsername("User"+i);
11            list.add(user);
12        }
13
14        ActionContext.getContext().put("users", list);
15        User u = new User();
16        u.setUsername("赵毅");
17        ActionContext.getContext().getValueStack().push(u);
18        return "detail";
19     }

对应的JSP代码如下:

1:  <s:property value="username"/> <br/>
2:  <s:iterator value="#users">
3:     <s:property value="username"/>
4:     <s:property value="#root[2].username"/><br/>
5:  </s:iterator>
6:  <s:property value="username"/>
7:  <s:property value="#root[1].username"/> <!-- 张三 -->

  根据刚才的示例,我们知道,第1行的username是“赵毅”(因为JSP在执行这行代码的时候,CompoundRoot中有两个元素:第0个是“user对象赵毅”,第1个是“userAction对象张三”),因此第1行的username将取出CompoundRoot中第0个元素的username属性:赵毅

第2行代码是iterator标签,只定义了一个value属性,iterator标签将循环访问users这个List中的User对象,并把当前循环的user对象压入到CompoundRoot中!所以,在第3行和第4行代码被执行的时候,CompoundRoot中总共有3个元素:第0个元素是被iterator标签压入的当前循环的user对象;第1个元素是“user对象赵毅”;第2个元素是“userAction对象张三”,因此第3行代码的执行结果就是输出“UserX”,即当前循环的user对象的username属性!iterator标签将会依次取出List中的user对象,并不断压入/弹出user对象(每次循环,都将执行一遍压入/弹出)。而第4行代码取第2个元素的username属性,即userAction对象的username属性:张三。第5行代码执行完成之后,在CompoundRoot中将剩下2个元素,与第2行代码被执行之前一样。所以,第6行代码的输出和第1行代码的输出结果是一样的,而第7行代码将取出userAction对象的username属性:张三

时间: 2024-11-05 20:29:11

Struts2传值的背后机制:Value Stack(值栈)的相关文章

Struts 中 ActionContext ctx.put()把数据放到ValueStack里之数据传输背后机制:ValueStack(值栈)

1.     数据传输背后机制:ValueStack(值栈) 在这一切的背后,是因为有了ValueStack(值栈)! ValueStack基础:OGNL要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)! OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性:它用于界面将参数传递到Action(并进行类型转换)中:它还可以用于struts2的配置文件中!所以,非常有必要理

[转]Struts2数据传输的背后机制:ValueStack(值栈)

1. 数据传输背后机制:ValueStack(值栈) 在这一切的背后,是因为有了ValueStack(值栈)! 2. ValueStack基础:OGNL 要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)! OGNL是Struts2中使用的一种表达式语言,它可以: ①用于JSP的标签库中,以便能够方便的访问各种对象的属性: ②用于界面将参数传递到Action(并进行类型转换)中: ③用于struts2的配置文件中! 所以,非常有

Struts2学习记录-Value Stack(值栈)和OGNL表达式

只是学习记录,把我知道的都说出来 一,值栈的作用 记录处理当前请求的action的数据. 二,小例子 有两个action:Action1和Action2 Action1有两个属性:name和passwd Action2有两个属性:name2和passwd2 请求从Action1进入,chain方法导向Action2 Struts.xml配置信息: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE st

Struts2数据传输的背后机制:ValueStack(值栈)

在介绍传值机制之前,首先了解OGNL,OGNL是ValueStack的基础. 要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)! OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性:它用于界面将参数传递到Action(并进行类型转换)中:它还可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本机制. OGNL介绍 OGNL是Object-Graph N

【Struts2】剖析Struts2中的反射技术 ValueStack(值栈)

1,Struts2框架主要组件的处理流程 在说ValueStack之前,笔者先说一说Struts2中常用的组件,struts2中常用组件有strutsPrepareAndExecuteExceptionn,以及一般执行流程: 请求来进入 Filter 控制器 Filter 控制器创建 ValueStack 对象并初始化 Filter 控制器根据 struts.xml 调用 defaultStack 拦截器栈 Filter 控制器根据 struts.xml 调用 Action 处理 Filter

Struts2的整体回顾(Action, 拦截器, 值栈, OGNL表示式, ModelDriven)

ValueStack里有map(request, session, attr, parameters)和对象栈. Map调用的方法: ActionContext.getContext().put(k,v)/ get(k) 对象栈的调用方法: ActionContext.getContext.getValueStack().push(obj)/pop(); OGNL格式: struts.xml, OGNL的格式为: ${ognl} jsp里 OGNL的格式为: %{ognl} 语法: user=f

Struts2的值栈和对象栈

ValueStack 如何得到值栈: 如何将对象存入值栈: 让值栈执行表达式来获得值: 在JSP中跳过栈顶元素直接访问第二层: 在JSP中访问值栈对象本身(而不是它们的属性) ActionContext ValueStack与ActionContext的联系和区别: 如何获得ActionContext: 如何向ActionContext中存入值: 如何从ActionContext中读取值: HttpServletRequest类或request的Map 使用HttpServletRequest类

Struts2 之值栈

值栈(ValueStack) http://www.cnblogs.com/bgzyy/p/8639893.html 这是我的有关 struts2 的第一篇文章,对于里面我们说到的一个 struts2 HelloWorld 小练习,即在输入框输入信息提交后在另外一个页面显示输入的信息,显示页面的代码如下: UserName: ${userName}<br> Email: ${email}<br> Address: ${address}<br> 为什么这样一个简单的标签就

struts2值栈分析

前段日子对ognl表达式不是很理解,看了几本书上关于ognl表达式的描述后还是感觉很难,前几天学习了struts2中值栈的内容,现在感觉ognl表达式其实很容易. struts2中利用值栈来存储数据,在struts2中,把数据存在值栈中,然后在jsp页面用ognl表达式取出. 在struts2中的action有三种方法获取值栈 ValueStack valueStack=ActionContext.getContext().getValueStack(); ValueStack valueSta