Struts2-OGNL

OGNL表达式语言介绍

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。  

OGNL相对其它表达式语言具有下面几大优势:  
    1. 支持对象方法调用,如xxx.doSomeSpecial();  
    2、支持类静态的方法调用和值访问,表达式的格式:
      @[类全名(包括包路径)]@[方法名 |  值名],例如:
      @java.lang.String@format('foo %s', 'bar')
      或@tutorial.MyConstant@APP_NAME;</pre>
    3、支持赋值操作和表达式串联,如price=100, discount=0.8,
    calculatePrice(),这个表达式会返回80;   
    4、访问OGNL上下文(OGNL context)和ActionContext;   
    5、操作集合对象。

Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了Java.utils.Map的接口.

理解Struts2中的 ValueStack:

    ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。  
    ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一个
                              ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前
                              Action 对象和其他相关对象.

Struts 框架把 ValueStack 对象保存在名为 “struts.valueStack” 的请求属性中,request中。

    在 ValueStack 对象的内部有两个逻辑部分:  
            ObjectStack: Struts  把Action和相关对象压入 ObjectStack 中--List  
            ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中  
                Struts 会把下面这些映射压入 ContextMap 中  
                        parameters: 该 Map 中包含当前请求的请求参数
                        request: 该 Map 中包含当前 request 对象中的所有属性
                        session: 该 Map 中包含当前 session 对象中的所有属性
                        application:该 Map 中包含当前 application  对象中的所有属性
                        attr: 该 Map 按如下顺序来检索某个属性: request, session, application

理解OGNL Context:

            OgnlValueStack 类包含两个重要的属性   一个root(对象栈)和一个context(Map栈)。  

   *    其中root(对象栈,即CompundRoot类型)本质上是一个ArrayList.  
        里边保存各种和当前 Action 实例相关的对象.
        且默认将当前Action对象压入栈顶,如果action中没有手动将任何对象压入栈顶,
        那么栈顶对象一直是当前Action对象。

   *    而context 是一个Map(更确切的说是一个OgnlContext对象)
        也是对 ActionContext 的一个引用. 里边保存着各种 Map:
        requestMap, sessionMap, applicationMap, parametersMap, attr
  在这个OgnlContext对象(context)中,有一个默认的顶层对象 \_root,OGNL访问context中这个默认顶层对象中的元素时,是不需要#号的,直接通过元素的名称来进行访问,

而访问其他对象时,如 request、session、attr等,则需要#号引用。

 注:Struts2将OgnlValueStack的root对象赋值给了OgnlContext 中的_root对象,  
    在OgnlValueStack的root对象中,保存着调用Action的实例,因此,在页面上通过Struts2标签访问Action 的属性时,就不需要通过#号来引用  

 总结:ognl Context包含 ObjectStack属性和ContextMap属性

  当Struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action 。然后把action存放进ValueStack,所以action的实例变量可以被OGNL访问。 

注意: Struts2中,OGNL表达式需要配合Struts标签才可以使用。如:<s:property value="name"/>

读取值栈中的属性值

  • 访问对象属性不需要#号,访问Map中的属性需要加#号
  • 对于对象栈: 对象栈中某一个对象的属性值
    • 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:
      • object.propertyName ; object['propertyName'] ; object["propertyName"]
    • ObjectStack 里的对象可以通过一个从零开始的下标来引用. ObjectStack 里的栈顶对象可以用 [0] 来引用,
      它下面的那个对象可以用 [1] 引用. [0].message:表示取第一个对象的message属性值
    • [n]的含义是从第n个对象开始搜索,但不限于第n个,如果第n个中没有,会到n+1个对象中找,依次类推下去,如果所有对象中都没找到,那就真的没有了。
    • 若从栈顶对象开始搜索, 则可以省略下标部分: message
    • 结合 s:property 标签: <s:property value="[0].message" /> 等价于 <s:property value="message" />
    • 默认情况下, Action 对象会被 Struts2 自动的放到值栈的栈顶.
  • Map 栈: request, session, application 的一个属性值 或 一个请求参数的值.
    • 使用<s:property value=""/>标签取值,如:
      ActionContext.getContext().put("myTest","HelloWorld");
      <s:property value="#request.myTest"/>
      取request中myTest这个key对应的value。
      ActionContext.getContext().put("test",test);
      <s:property value="#request.test.productName"/>
      EL方式取:${requestScope.test.productName}
      取request中test这个key对应test对象的productName属性值。
      注:其中#requet也可以不写,<s:property value="#test.productName"/>

取值示例

区分是对象站还是Map栈看使用的方法时push(Object)还是put(String,Object),前者是放到对象栈,后者放到Map栈。
    /**测试Map栈*/
    public String testBean() {
        ActionContext context = ActionContext.getContext();
        Ognl ognl = new Ognl();
        ognl.setName("zhangsan");
        ognl.setDescription("是个好人");
        context.put("singleBean", ognl);
        context.put("singleBean2", "testAction2");

//       <s:Property value="#request.singleBean3">
//       ServletActionContext.getRequest()
//                      .setAttribute("singleBean3","testAction3");

        return "success";
    }

    /**测试对象栈*/
    public String testObjectStack() {
        // 把一个对象放入到栈顶
        Ognl ognl = new Ognl();
        ognl.setName("lisi");
        ActionContext.getContext().getValueStack().push(ognl);
        // 或者
        ActionContext.getContext().getValueStack().getRoot().add(0, ognl);

        // 获取栈顶元素
        ActionContext.getContext().getValueStack().peek();
        // 或者
        ActionContext.getContext().getValueStack().getRoot().get(0);

        // 弹栈
        ActionContext.getContext().getValueStack().pop();

        // 改变对象栈中的某个元素的值
        ActionContext.getContext().getValueStack().setValue("name", "aaa");
        ActionContext.getContext().put("bbb", "bbbb");

        String name = ActionContext.getContext().getValueStack()
                .findValue("name").toString();
        String bbb = ActionContext.getContext().getValueStack().findValue("bbb")
                .toString();
        System.out.println(name);
        System.out.println(bbb);

        return "success";
    }


    <!-- EL获取key为singleBean对象的name属性 -->
    actionContext.put : ${requestScope.singleBean.name} <br>
    <!-- EL获取key为singleBean2的value值 -->
    actionContext.put2 : ${requestScope.singleBean2} <br>
    <!-- OGNL获取key为singleBean对象的name属性 -->
    actionContext.put3 : <s:property value="#request.singleBean.name"/><br>
    <!-- OGNL获取key为singleBean2的value值 -->
    actionContext.put4 : <s:property value="singleBean2"/><br>
    /**测试迭代List*/
    public String testList() {
        List<Ognl> list = new ArrayList();
        Ognl ognl = new Ognl();
        ognl.setName("testList1");
        ognl.setDescription("testList111");
        list.add(ognl);
        Ognl ognl2 = new Ognl();
        ognl.setName("testList2");
        ognl.setDescription("testList222");
        list.add(ognl2);
        ActionContext.getContext().put("testList", list);
        return "success";
    }

    <!-- 迭代 List -->
    <s:iterator value="#testList">
        <!-- 直接用对象属性取值 -->
        <s:property value="name"/>
        <s:property value="description"/>
    </s:iterator>
    /**测试迭代Map*/
    public String testMap() {
        Map<String, Ognl> map = new HashMap();
        Ognl ognl = new Ognl();
        ognl.setName("开发部");
        ognl.setDescription("程序员比较多");
        map.put("a", ognl);
        Ognl ognl2 = new Ognl();
        ognl.setName("UI部");
        ognl.setDescription("女生比较多");
        map.put("b", ognl2);
        ActionContext.getContext().put("testMap", map);

        return "success";
    }

    <!-- 迭代 Map<String,Ognl> -->
    <s:iterator value="#testMap">
        <s:property value="key"/>
        <!-- 迭代Map其实就是迭代Map中的Entry。
            Map.Entry是Map声明的一个内部接口,此接口为泛型,定义为Entry<K,V>。
                        它表示Map中的一个实体(一个key-value对)。接口中有getKey(),getValue方法。
                       上面的value="key" -> entry.getKey()。 
                       下面的value=”value.name" -> entry.getValue().name            
        -->
        <s:property value="value.name"/>
    </s:iterator>
    /**测试listMap*/
    public String testListMap() {
        List<Map<String, Ognl>> listMap = new ArrayList();
        Map<String, Ognl> map = new HashMap();
        Ognl ognl = new Ognl();
        ognl.setName("开发部");
        ognl.setDescription("程序员比较多");
        map.put("a", ognl);
        Ognl ognl2 = new Ognl();
        ognl.setName("UI部");
        ognl.setDescription("女生比较多");
        map.put("b", ognl2);

        listMap.add(map);

        ActionContext.getContext().put("testListMap", listMap);

        return "success";
    }

    <!-- 迭代List<Map<String,Ognl>> -->
    <s:iterator value="#testListMap"><!-- 第一个是迭代List -->
        <s:iterator><!-- 第二个是迭代Map -->
            <s:property value="key"/>
            <s:property value="value"/>
        </s:iterator>
    </s:iterator>
    /**测试mapList*/
    public String testMapList() {
        Map<String, List<Ognl>> mapList = new HashMap();
        List<Ognl> list = new ArrayList();
        Ognl ognl = new Ognl();
        ognl.setName("testList1");
        ognl.setDescription("testList111");
        list.add(ognl);
        Ognl ognl2 = new Ognl();
        ognl.setName("testList2");
        ognl.setDescription("testList222");
        list.add(ognl2);

        mapList.put("ml", list);

        ActionContext.getContext().put("testMapList", mapList);

        return "success";
    }
    <!-- 迭代Map<String,List<Ognl>> -->
    <s:iterator value="#testMapList">
        <s:property value="key"/>
        <s:iterator value="value">
            <s:property value="name"/>
            <s:property value="description"/>
        </s:iterator>
    </s:iterator>
    /**测试listMapList*/
    public String testListMapList() {
        List<Map<String, List<Ognl>>> listMapList = new ArrayList();
        Map<String, List<Ognl>> mapList = new HashMap();
        List<Ognl> list = new ArrayList();
        Ognl ognl = new Ognl();
        ognl.setName("testList1");
        ognl.setDescription("testList111");
        list.add(ognl);
        Ognl ognl2 = new Ognl();
        ognl.setName("testList2");
        ognl.setDescription("testList222");
        list.add(ognl2);

        mapList.put("ml", list);
        listMapList.add(mapList);

        ActionContext.getContext().put("testListMapList", listMapList);

        return "success";
    }

   <!-- 迭代List<Map<String,List<Ognl>>> -->
   <s:iterator value="#testListMapList"><!-- 最外层list -->
        <s:iterator><!-- map -->
            <s:property value="key"/>
            <s:iterator value="value"><!-- map中的list -->
                <s:property value="name"/>
                <s:property value="description"/>
            </s:iterator>
        </s:iterator>
   </s:iterator>
取值原则:当前栈顶对象是什么,
    如果是list,那么就是list中的第一个元素。
    如果是map,那么就是第一个entry。
       value="key" : 取出map 中 key 的值
       value="value" : 取出map中 key所对应的value值
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容