• Struts2中的ActionContext、OGNL及EL的使用


    文章分类:Java编程

    本文基于struts2.1.8.1,xwork2.1.6
    1.EL
            EL(Expression Language)源于jsp页面标签jstl,后来被jsp2.0直接支持,因此可以在jsp页面中直接使用EL表达式。其使用语法为${expr},如${username},表达式expr中变量的获取,默认使用PageContext.findAttribute(String)方法,也就是从pageContext隐藏对象中查找,pageContext隐藏对象包含以下隐藏对象:request,response,session,servletContext.查找范围顺序是page, request,session, and application,找不到则返回null。指定对象查找则使用${implictObject.foo}语法,其中implictObject代表任意隐藏对象,除pageContext之外,还包含:param,paramValues,header,headerValues,cookie,initParam以及pageScope,requestScope,sessionScope,applicationScope等。
            注意:自定义变量如果和隐藏对象同名,${implicitObject}将返回隐藏对象,而不是自定义对象的值。

    2.OGNL in Struts2
            struts2使用ActionContext作为OGNL的上下文,也就是ActionContext.context,其默认的root上下文是一个OGNL的值栈(OgnlValueStack),用于存放action的实例,其访问不需要标记,同时存放了其它的对象,如request,parameters,session,application,attr等,访问需要#标记并指明对象名称,如#session.username.由于ActionContext存放到ThreadLocal中,所以是线程安全的。在同一个请求经过不同的action转发(forward)处理的过程中,这些action实例都会压进root这个值栈中,最后的在最上面,查找变量值也是从上面开始顺序往下。
            ActionContext是由org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter进行初始化的,在doFilter中调用prepare.createActionContext(request, response);追踪下去,核心代码在org.apache.struts2.dispatcher.ng.PrepareOperations中:

    Java代码

    1. public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {   
    2.         ActionContext ctx;   
    3.         Integer counter = 1;   
    4.         Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);   
    5.         if (oldCounter != null) {   
    6.             counter = oldCounter + 1;   
    7.         }   
    8.            
    9.         ActionContext oldContext = ActionContext.getContext();   
    10.         if (oldContext != null) {   
    11.             // detected existing context, so we are probably in a forward   
    12.             ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));   
    13.         } else {   
    14.             ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();   
    15.             stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));   
    16.             ctx = new ActionContext(stack.getContext());   
    17.         }   
    18.         request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);   
    19.         ActionContext.setContext(ctx);   
    20.         return ctx;   
    21.     }  

    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {

            ActionContext ctx;

            Integer counter = 1;

            Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);

            if (oldCounter != null) {

                counter = oldCounter + 1;

            }

           

            ActionContext oldContext = ActionContext.getContext();

            if (oldContext != null) {

                // detected existing context, so we are probably in a forward

                ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));

            } else {

                ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();

                stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));

                ctx = new ActionContext(stack.getContext());

            }

            request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);

            ActionContext.setContext(ctx);

            return ctx;

        }



    3.JSP中的使用
            JSP2.0可以直接使用EL表达式,方便变量的获取。使用struts2的标签,可以取到ActionContext中的对象,如action中的成员变量,非root中的变量使用标记方式。
            另外,struts2标签中,非字符类型(如boolean,int型)的属性取值,需要使用%{expr}的方式。在JSP中也可以使用EL表达式获取Action中的成员属性,原因是struts2对request进行了封装(org.apache.struts2.dispatcher.StrutsRequestWrapper),重写了request.getAttribute()方法,查找范围扩大到了ActionContext的root中,核心代码如下:

    Java代码

    1. public Object getAttribute(String s) {   
    2.         if (s != null && s.startsWith("javax.servlet")) {   
    3.             // don't bother with the standard javax.servlet attributes, we can short-circuit this   
    4.             // see WW-953 and the forums post linked in that issue for more info   
    5.             return super.getAttribute(s);   
    6.         }   
    7.   
    8.         ActionContext ctx = ActionContext.getContext();   
    9.         Object attribute = super.getAttribute(s);   
    10.         if (ctx != null) {   
    11.             if (attribute == null) {   
    12.                 boolean alreadyIn = false;   
    13.                 Boolean b = (Boolean) ctx.get("__requestWrapper.getAttribute");   
    14.                 if (b != null) {   
    15.                     alreadyIn = b.booleanValue();   
    16.                 }   
    17.        
    18.                 // note: we don't let # come through or else a request for   
    19.                 // #attr.foo or #request.foo could cause an endless loop   
    20.                 if (!alreadyIn && s.indexOf("#") == -1) {   
    21.                     try {   
    22.                         // If not found, then try the ValueStack   
    23.                         ctx.put("__requestWrapper.getAttribute", Boolean.TRUE);   
    24.                         ValueStack stack = ctx.getValueStack();   
    25.                         if (stack != null) {   
    26.                             attribute = stack.findValue(s);   
    27.                         }   
    28.                     } finally {   
    29.                         ctx.put("__requestWrapper.getAttribute", Boolean.FALSE);   
    30.                     }   
    31.                 }   
    32.             }   
    33.         }   
    34.         return attribute;   
    35.     }  

    一.Xwork的OGNL
    在Struts2里边的OGNL是基于XWork的。XWork的OGNL和普通意义上的OGNL有一些差别,首先最大的差别就是OGNL只提供了一个根对象(root),而Xwork提供了一个ValueStack,这是Struts2的OGNL的默认root。另外,XWork提供了自己独特的OGNL PropertyAccessor自动从顶向下的查找Stack中符合条件的对象属性。
    比如说,有两个对象Animal和Person,两个对象都提供了name属性,同时Animal有species属性,Person有salary属性。假定Animal在stack的顶端。

    源码copy to clipboard打印

    1. species // call to animal.getSpecies()    
    2.   
    3. salary // call to person.getSalary()    
    4.   
    5. name // call to animal.getName() because animal is on the top  

    species // call to animal.getSpecies()

     

    salary // call to person.getSalary()

     

    name // call to animal.getName() because animal is on the top


    从这里可以看到从顶向下查找的作用。在XWork中,我们可以用[n].name这种方式来访问不同层次的同名属性。n必须是正整数。

    源码copy to clipboard打印

    1. [0].name // call to animal.getName()    
    2. [1].name // call to person.getName()  

    [0].name // call to animal.getName()

    [1].name // call to person.getName()


    访问对象的静态属性的时候,OGNL默认采用如下的方式

    源码copy to clipboard打印

    1. @some.package.ClassName@FOO_PROPERTY    
    2. @some.package.ClassName@someMethod()  

    @some.package.ClassName@FOO_PROPERTY

    @some.package.ClassName@someMethod()


    而XWork访问静态属性的方式有所差异:通过@vs来访问。vs表示的意思是valueStack。并且,可以在vs后面跟随数字来表示其访问stack的层次。

    源码copy to clipboard打印

    1. @vs@FOO_PROPERTY    
    2. @vs@someMethod()    
    3.   
    4. @vs1@FOO_PROPERTY    
    5. @vs1@someMethod()    
    6.   
    7. @vs2@BAR_PROPERTY    
    8. @vs2@someOtherMethod()  

    @vs@FOO_PROPERTY

    @vs@someMethod()

     

    @vs1@FOO_PROPERTY

    @vs1@someMethod()

     

    @vs2@BAR_PROPERTY

    @vs2@someOtherMethod()


    二.Struts2中的OGNL
    在Struts2中,采用标准命名的上下文(Context)来处理OGNL表达式。处理OGNL的顶级对象是一个Map(也叫context map),而OGNL在这个context中就是一个顶级对象(root)。在用法上,顶级对象的属性访问,是不需要任何标记前缀的。而其它非顶级的对象访问,需要使用#标记。
    Struts2框架把OGNL Context设置为我们的ActionContext。并且ValueStack作为OGNL的根对象。除value stack之外,Struts2框架还把代表application、session、request这些对象的Map对象也放到ActionContext中去。(这也就是Struts2建议在Action类中不要直接访问Servlet API的原因,他可以通过ActionContext对象来部分代替这些功能,以方便对Action类进行测试!)
    Action的实例,总是放到value stack中。因为Action放在stack中,而stack是root(根对象),所以对Action中的属性的访问就可以省略#标记。但是,要访问ActionContext中其它对象的属性,就必须要带上#标记,以便让OGNL知道,不是从根对象,而是从其它对象中去寻找。
    那么访问Action中的属性的代码就可以这样写

    <s:property value="postalCode"/>
    其它ActionContext中的非根对象属性的访问要像下面这样写:
    <s:property value="#session.mySessionPropKey"/> or
    <s:property value="#session['mySessionPropKey']"/> or
    <s:property value="#request['myRequestPropKey']"/>
    对Collection的处理,内容就很简单。
    <s:select label="label" name="name" list="{'name1','name2','name3'}" value="%{'name2'}" />
    这是处理List。这个代码在页面上建立一个下拉选项,内容是list中的内容,默认值是name2.
    处理map

     <s:select label="label" name="name" list="#{'foo':'foovalue', 'bar':'barvalue'}" />

     需要注意的是,判断一个值是否在collection中。我们要使用in或者not in来处理。
    <s:if test="'foo' in {'foo','bar'}">
       muhahaha
    </s:if>
    <s:else>
       boo
    </s:else>
     另外,可以使用通配符来选择collection对象的子集。
     ?——所有匹配选择逻辑的元素
     ^——只提取符合选择逻辑的第一个元素
     $——只提取符合选择逻辑的最后一个元素
    person.relatives.{? #this.gender == 'male'}

  • 相关阅读:
    has a / is a 的区别
    Linux头文件作用
    转一篇Decorator模式的讲解文章
    歌手推荐kate st. john
    拷贝构造函数和赋值构造函数声明为私有的作用
    重新认识C++中new的用法
    系统程序员成长计划容器与算法(二)(下)
    深入C++的new
    歌手推荐Cara Dillon
    浅析一道C++设计面试题
  • 原文地址:https://www.cnblogs.com/nucdy/p/5084889.html
Copyright © 2020-2023  润新知