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'}

原文地址:https://www.cnblogs.com/nucdy/p/5084889.html