`
wangming2012
  • 浏览: 141073 次
  • 性别: Icon_minigender_1
  • 来自: 枣阳
社区版块
存档分类
最新评论

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

阅读更多

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

 

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

 

ValueStack基础:OGNL

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

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

 

Root对象

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

package cn.com.leadfar.struts2.actions;

 

publicclass User {

    private String username;

    private Group group;

   

    public String getUsername() {

       returnusername;

    }

    publicvoid setUsername(String username) {

       this.username = username;

    }

   

    public Group getGroup() {

       returngroup;

    }

    publicvoid setGroup(Group group) {

       this.group = group;

    }

}

 

package cn.com.leadfar.struts2.actions;

 

publicclass Group {

    private String name;

    private Organization org;

    public String getName() {

       returnname;

    }

 

    publicvoid setName(String name) {

       this.name = name;

    }

 

    public Organization getOrg() {

       returnorg;

    }

 

    publicvoid setOrg(Organization org) {

       this.org = org;

    }

}

 

package cn.com.leadfar.struts2.actions;

 

publicclass Organization {

    private String orgId;

 

    public String getOrgId() {

       returnorgId;

    }

 

    publicvoid setOrgId(String orgId) {

       this.orgId = orgId;

    }

}

 

 

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

 

User(root)

   -- username

   -- group

      -- name

      -- org

         -- orgId

 

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

 

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

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

        o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

 

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

 

//JAVA来导航访问

user.getGroup().getOrg().getOrgId();

 

 

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

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

 

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

 

       //利用OGNL表达式访问

       String value = (String)Ognl.getValue("group.org.orgId", user);

 

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

完整代码如下:

    publicvoid testOgnl01() throws Exception{

       User user = new User();

       user.setUsername("张三");

      

       //利用OGNL表达式访问user对象的username属性

       String value = (String)Ognl.getValue("username", user);

       log(value);

    }

   

    publicvoid testOgnl02() throws Exception{

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

      

       //JAVA来导航访问

       log(user.getGroup().getOrg().getOrgId());

      

       //利用OGNL表达式访问

       String value = (String)Ognl.getValue("group.org.orgId", user);

       log(value);

    }

   

    publicvoid testOgnl03() throws Exception{

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

      

       //JAVA来导航访问

       log(user.getGroup().getOrg().getOrgId());

      

       //也可以在表达式中使用#root来代表root对象

       String value = (String)Ognl.getValue("#root.group.org.orgId", user);

       log(value);

    }

    privatevoid log(Object o){

       System.out.println(o);

    }

 

 

Context对象

OGNL的表达式中,有可能需要访问到多个毫不相干的对象,这时候,我们需要给OGNL传递一个Map类型的对象,把表达式中需要用到的对象放到Map中即可!这个Map对象,称为context

 

要在表达式中访问到context中的对象,需要使用“#对象名称”的语法规则。

如:

    publicvoid testOgnl04() throws Exception{

       User user = new User();

       user.setUsername("张三");

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

      

       User user2 = new User();

       user2.setUsername("李四");

      

       /**

        *所谓context其实就是一个Map类型的对象。主要是因为在OGNL中,不支持多个root对象,那么

        *如果需要在表达式中访问更多毫不相干的对象时,只能通过一个Map来把这些对象统一传递给OGNL

        */

       Map context = new HashMap();

       context.put("u1", user);

       context.put("u2", user2);

      

       //在表达式中需通过“#+对象的名称来访问context中的对象

       //如果表达式中没有用到root对象,那么可以用任意一个对象代表root对象!

       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username", context,new Object());

       log(value);

    }

   

    publicvoid testOgnl05() throws Exception{

       User user = new User();

       user.setUsername("张三");

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

      

       User user2 = new User();

       user2.setUsername("李四");

      

       User user3 = new User();

       user3.setUsername("王五");

      

       Map context = new HashMap();

       context.put("u1", user);

       context.put("u2", user2);

      

       //OGNL传递root对象及context对象以便解释对应的表达式

       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username + ',' + username", context,user3);

       log(value);

    }

 

利用OGNL表达式进行赋值

 

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

    publicvoid testOgnl06() throws Exception{

       User user = new User();

      

       //调用setValue()方法来进行赋值

       //第一个参数:OGNL表达式

       //第二个参数:root对象

       //第三个参数:要赋的值     

       Ognl.setValue("username", user, "张三");

 

       log(user.getUsername());

    }

   

    publicvoid testOgnl07() throws Exception{

       User user = new User();

      

       Map context = new HashMap();

       context.put("u", user);

      

       //调用setValue()方法来进行赋值

       //第一个参数:OGNL表达式

       //第二个参数:context对象

        //第三个参数:root对象

       //第四个参数:要赋的值

       Ognl.setValue("#u.username", context, new Object(), "张三");

 

       log(user.getUsername());

    }

   

    publicvoid testOgnl08() throws Exception{

       User user = new User();

      

       Map context = new HashMap();

       context.put("u", user);

       

       //利用赋值符号"="来进行赋值

       Ognl.getValue("#u.username = '李四'", context, new Object());

 

       log(user.getUsername());

    }

   

    publicvoid testOgnl09() throws Exception{

       User user1 = new User();

       User user2 = new User();

       Map context = new HashMap();

       context.put("u1", user1);

       context.put("u2", user2);

      

       //在一个表达式中可以用逗号分隔,同时执行多个表达式

       Ognl.getValue("#u1.username = '李四',#u2.username='王五'", context, new Object());

 

       log(user1.getUsername());

       log(user2.getUsername());

    }

 

利用OGNL调用对象的方法

 

    //************************* OGNL调用对象的方法 *****************************//

    publicvoid testOgnl10() throws Exception{

       User user = new User();

      

       //如果是调用root对象的方法,可以直接使用方法的名称来调用方法

       Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);

       log(value);

    }

   

    publicvoid testOgnl11() throws Exception{

       User user = new User();

       user.setUsername("李四");

       //如果是调用root对象的方法,可以直接使用方法的名称来调用方法

       String value = (String)Ognl.getValue("getUsername()", user);

       log(value);

    }

   

    publicvoid testOgnl12() throws Exception{

       User user = new User();

       Ognl.getValue("setUsername('王五')", user);

       String value = (String)Ognl.getValue("getUsername()", user);

       log(value);

    }

   

    //************************* OGNL调用静态方法和变量 *********************//

    publicvoid testOgnl13() throws Exception{

       User user = new User();

       user.setUsername("王五");

       //调用静态变量

       //注意:outSystem中的静态变量,outPrintStream类型的一个对象

       //println()则是out这个对象中的实例方法(不是静态方法)

       //调用静态方法,需要在类名和变量名前面加上@来调用,对于实例方法,用"."来调用

       Ognl.getValue("@System@out.println(username)", user);

    }

   

    publicvoid testOgnl14() throws Exception{

       User user = new User();

       user.setUsername("wangwu");

       //调用静态方法,注意使用全路径类名

        Ognl.getValue("@System@out.println(@cn.com.leadfar.utils.Utils@toUpperCase(username))", user);

    }

 

利用OGNL访问数组、集合对象

 

    publicvoid testOgnl15() throws Exception{

      

       Object root = new Object();

       Map context = new HashMap();

      

       //利用OGNL创建java.util.List对象

       List list = (List)Ognl.getValue("{123,'xxx','kdjfk'}", context, root);

       context.put("list", list);

      

       //利用OGNL创建数组

       int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);

       context.put("intarray", intarray);

      

       //利用OGNL表达式创建java.util.Map对象

       Map mapvalue = (Map)Ognl.getValue("#{'listvalue':#list,'intvalue':#intarray}", context, root);

       context.put("mapvalue", mapvalue);

      

       //利用OGNL表达式访问这些数组和集合对象

       Ognl.getValue("@System@out.println(#list[1])", context,root);

       Ognl.getValue("@System@out.println(#intarray[2])", context,root);

       Ognl.getValue("@System@out.println(#mapvalue.listvalue[0])", context,root);

       Ognl.getValue("@System@out.println(#mapvalue['intvalue'][0])", context,root);

    }

   

    publicvoid testOgnl16() throws Exception{

      

       List root = new ArrayList();

       User user1 = new User();

       user1.setUsername("张三");

       User user2 = new User();

       user2.setUsername("李四");

       root.add(user1);

       root.add(user2);

      

       //如果root对象是List类型

       log(Ognl.getValue("#root[0].username", root));

       log(Ognl.getValue("#root[1].username", root));

    }

 

 

 

更多的特性,请参考官方的文档

 

OGNL官方文档地址:http://www.opensymphony.com/ognl/html/LanguageGuide/index.html

 

应用:ValueStack

 

理解ValueStack的基本机制!对各种现象作出解释。

ValueStack实际上就是对OGNL的封装,OGNL主要的功能就是赋值与取值,Struts2正是通过ValueStack来进行赋值与取值的!

 

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

void setValue(String expr, Object value);

Object findValue(String expr);

用来通过OGNL表达式对ValueStack中的数据进行操作!

 

ValueStack中的root对象是CompoundRootCompoundRoot继承了ArraryList,提供了额外的方法:push()pop()方法,用来对root对象中所包含的数据进行存取!

publicclass CompoundRoot extends ArrayList {

 

    public CompoundRoot() {

    }

 

    public CompoundRoot(List list) {

        super(list);

    }

 

 

    public CompoundRoot cutStack(int index) {

        returnnew CompoundRoot(subList(index, size()));

    }

 

    public Object peek() {

        return get(0);

    }

 

    public Object pop() {

        return remove(0);

    }

 

    public void push(Object o) {

        add(0, o);

    }

}

 

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

 

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

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

 

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

publicclass UserAction {

    private String username;

    private Integer age;

    privatebooleanvalid;

   

    //查看用户的详细信息

    public String detail(){

      

       username = "张三";

       age = 18;

       valid = true;

      

       return"detail";

    }

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

username:<s:property value="username"/><br/>

valid:<s:property value="valid"/><br/>

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()方法,将继续搜索第123……个元素是否有getUsername()方法。

 

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

 

再看下面的例子:

publicclass UserAction {

    private String username;

    private String name;

   

    //查看用户的详细信息

    public String detail(){

       username = "张三";

       name = "王五";

      

       User u = new User();

       u.setUsername("赵毅");

       ActionContext.getContext().getValueStack().push(u);

      

       return"detail";

    }

在上面这个UserAction的代码中,我们直接调用ActionContext.getContext().getValueStack().push()方法,把一个User对象(这个对象拥有getUsername()setUsername()方法)直接压入到ValueStack中,这时候,在ValueStackCompoundRoot中将有两个元素:第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属性了!

 

再看下面的代码:

publicclass UserAction {

    private String username;

   

    //查看用户的详细信息

    public String detail(){

       username = "张三";

      

       List list = new ArrayList();

       for(int i=0; i<10; i++){

           User user = new User();

           user.setUsername("User"+i);

           list.add(user);

       }

       ActionContext.getContext().put("users", list);

      

       User u = new User();

       u.setUsername("赵毅");

       ActionContext.getContext().getValueStack().push(u);

      

       return"detail";

    }

 

对应的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属性:张三

分享到:
评论
1 楼 sinotao1 2013-04-20  
能够讲到这个地步的,struts2已经很通透了。

相关推荐

    在线培训:ValueStack

    【在线培训:ValueStack】是一场深入探讨Java...通过深入学习,开发者将能够熟练掌握栈数据结构的运用,理解Struts2框架中的ValueStack机制,并能灵活地调整和操作ValueStack,提高项目开发的效率和代码的可维护性。

    Struts2值栈的理解

    Struts2 值栈是 Struts2 框架中的一种核心机制,负责存储和管理 Action 中的数据。在 Struts2 中,每个 Action 类都有一个 ValueStack 对象,该对象贯穿整个 Action 的生命周期。在 Struts2 接收到一个.action 的...

    ValueStack使用参考

    2. **参数绑定**:Struts2使用OGNL将请求参数自动绑定到Action实例的属性上,这些参数可以通过ValueStack进行访问。 3. **Action执行**:Action实例执行其业务逻辑,过程中可以使用栈中的数据,也可以修改栈中的...

    Struts2中关于ValueStack的一些操作

    1、 ValueStack其实就是一个放置Java对象的堆栈而已,唯一特别的是可以使用EL来获得值堆栈中对象属性的数据,并可以为值堆栈的对象属性赋值。 2、 EL,全称Express Language,即表达式语言。不要被语言吓倒,它是...

    Struts2工作机制

    Struts2中的`ValueStack`是一个对象容器,用于在Action和视图之间传递数据。Action的属性可以直接在视图中通过OGNL(Object-Graph Navigation Language)表达式访问,简化了视图和模型之间的数据交互。 7. **配置...

    Struts2和Struts1.x的全面比较

    - **Struts2**:支持模型驱动(ModelDriven)和值栈(ValueStack),数据绑定更为智能,减少代码量,提高可维护性。 4. **异常处理**: - **Struts1.x**:异常处理不够健壮,通常需要手动编写异常处理代码。 - *...

    struts1和struts2的区别

    它分为两个版本:Struts1 和 Struts2。虽然两者都基于模型-视图-控制器(MVC)设计模式,但它们之间存在诸多差异。了解这些差异对于选择合适的技术栈、优化开发流程至关重要。 #### Action处理机制 - **Struts1**:...

    linjin101#javaStudy#Struts2中的OGNL和值栈ValueStack1

    1、什么是值栈 2、值栈的内部结构 3、ActionContext和ValueStatck的关系 4、如何获取值栈对象 5、向值栈存数据 6、从值栈中获取数据

    struts2权威指南光盘源码-第18章源码

    - **ValueStack**:Struts2中的模型对象集合,用于在Action和视图之间传递数据。 - **Result**:定义了Action执行后如何展示结果,可以是JSP、FreeMarker模板或其他视图技术。 4. **Struts2的配置**: - XML配置...

    Struts 2 技术详解:基于WebWork核心的MVC开发与实践

    Struts 2 的核心技术基础是WebWork,它在Struts 1的基础上进行了许多改进,如更强大的表达式语言(OGNL)、拦截器机制、更简单的配置等。WebWork的这些特性被集成到Struts 2中,使得Struts 2成为了一个高效且易用的...

    struts2 标签使用

    在 Struts2 中,所有的请求都会被封装到一个叫做 `ValueStack` 的对象中。这个 `ValueStack` 包含了请求中的所有数据,包括表单提交的数据、从 Action 类返回的对象等。通过 OGNL 表达式,我们可以直接在 JSP 页面...

    转:struts1与struts2的区别

    - **Struts2**: 采用“ValueStack”技术,允许标签库访问值而无需显式绑定视图与对象。这种策略使得视图可以重用,即使它们引用的是不同类型的属性。 #### 8. 类型转换的能力 - **Struts1**: Struts1通常将`...

    struts1和struts2区别

    - **Struts2**:采用ValueStack技术,允许taglib直接访问视图,无需显式绑定,增强了重用性。 8. **类型转换**: - **Struts1**:依赖Commons-Beanutils进行类型转换,转换器不可配置且通常是针对String类型。 -...

    struts2数据类型转换器

    总的来说,了解和掌握Struts2的数据类型转换机制对于开发高效、健壮的Java Web应用至关重要。通过自定义转换器,我们可以灵活地控制数据的转换过程,确保应用程序能正确处理用户输入,避免因为类型不匹配导致的错误...

    马士兵Struts2笔记2013

    为了调试目的,可以开启Struts2的值栈调试功能,查看栈中的内容,帮助开发者了解当前请求的状态和数据流。 9. **Struts2访问Web元素** Struts2可以通过OGNL(Object-Graph Navigation Language)轻松地访问和操作...

    Struts2权威指南源码(完整),不含JAR文件

    4. **ValueStack**:值栈是Struts2的数据传递机制,它可以将Action的属性暴露给视图层,同时也支持OGNL(Object-Graph Navigation Language)表达式,方便数据访问。 5. **Result**:Result负责将Action执行后的...

    struts2从入门到精通总结(备忘)

    本总结将深入探讨Struts2的核心概念、配置、Action的创建、数据操作、值栈以及拦截器的使用,旨在帮助初学者快速上手并深入理解Struts2。 一、Struts2入门 Struts2作为Apache软件基金会的一个项目,是Struts1的升级...

    struts2jar包

    5. **struts2-plugins*.jar**:Struts2有许多插件,如struts2-dojo-plugin.jar用于与Dojo库集成,提供富客户端功能;struts2-junit-plugin.jar则帮助进行单元测试。这些插件根据实际需求选择引入。 6. **其他依赖的...

    [黑马程序员Struts2 2016版视频] 前3Days笔记

    1. FilterDispatcher:Struts2的核心控制器,负责拦截所有HTTP请求,判断是否由Struts2处理。 2. Action:业务逻辑的执行者,每个Action对应一个用户操作。 3. ActionMapping:映射Action与URL的关系,决定请求如何...

    精通Struts 2:Web 2.0开发实战

    《精通Struts 2:Web 2.0开发实战》这本书是Java Web开发领域的一本经典教程,专注于Struts 2框架的应用与实践。Struts 2作为一款强大的MVC(Model-View-Controller)框架,是Java EE平台上的重要组成部分,它极大地...

Global site tag (gtag.js) - Google Analytics