关于Struts2 中OGNL/值栈ValueStack的学习

1. OGNL

OGNL(Object-Graph Navigation Language,对象图导航语言)是一种强大的表达式语言,用于引用和操作值栈上的数据,还可用于数据传输和类型转换,使用它可以存取任意属性,调用对象中的方法。其作用的就是为了简化访问对象中的属性值。OGNL非常类似于JSP表达式语言。OGNL表达式的解析都是围绕OGNL上下文进行的,OGNL表达式是一个map对象,其中包含多个java对象,其中有一个对象十分的特殊,这个对象称为根对象,使用标记符号(即#号)来引用默认或根对象的属性。

a.一个OGNL表达式若没有指定目标对象,默认就是从根对象开始查找

b.若要访问非根对象中的属性,就要在访问根对象的前面需加#前缀

2.值栈 valueStack

(1)valueStack是值栈的对外接口,实际对应的类是OgnlValueStack,OgnlValueStack对象中包含了一个ArrayList集合来作为栈,默认action对象位置值栈的栈顶位置。

a.void push(Objext o) 可用来将一个对象数据压入栈顶.

b.set(String key,Objext obj) 则是将数据放入Map对象当中,并将Map对象压入栈顶位置!

(2) OGNL表达式在取数据的时候,其规则是先从栈的栈顶(action对象)中开始查找,先判断是否有Map对象,根据key取值;不是Map对象,则寻找该对象的getKey()方法,得到其值,若没有方法,则查找key变量(要求必须是public修饰)均为找到,就去第二个对象中向下查找
 

3.ActionContext

如前面所述,OGNL是基于上下文的,而Struts构建了一个ActionContext映射以供OGNL使用,即ActionContext就是OGNL的上下文,而根对象就是这个Action对象。 ActionContext映射包含以下内容:

  • 应用程序 - 应用程序作用域变量

  • 会话 - 会话作用域变量

  • 根/值栈 - 所有的action变量都存储在这里

  • 请求 - 请求作用域变量

  • 参数 - 请求参数

  • 属性 - 存储在页面,请求,会话和应用程序作用域中的属性

ActionContext是Action执行时的上下文,里面存放着Action在执行时需要用到的对象,也称之为广义值栈。Struts2在每次执行Action之前都会创建新的ActionContext,在同一个线程里ActionContext里面的属性是唯一的,这样Action就可以在多线程中使用。

1.1 ActionContext的线程安全性

那么Struts2是如何保证ActionContext的线程安全性呢?看看ActionContext对象的代码,示例如下

public class ActionContext implements Serializable {
    static ThreadLocal actionContext = new ThreadLocal();
    public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
    public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
    public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
    public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
    public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
    public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
    public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter";
    public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
    public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
    public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
    Map<String, Object> context;

    public ActionContext(Map<String, Object> context) {
        this.context = context;
    }
/**************************************more code***********************************/
}

ThreadLocal又称为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。存放在ActionContext里的数据都存放在这个ThreadLocal的属性中,而这个属性只会在对应的当前请求线程中可见,从而保证数据是线程安全的。

1.2 访问的是Map

  Struts2框架将与Web相关的很多对象重新进行了包装,比如将HttpSession对象重新包装成了一个Map对象,里面存放着Session中的数据,提供这个Map给Action使用,而不用Action直接和底层的HttpSession打交道。也正是因为框架的包装,让Action可以完全的和Web层解耦。但是要注意一点,ActionContext不能在普通的Java应用程序中使用。我们知道Action和Servlet API是解耦的,因此可以在Java应用程序中调用Action的execute方法来进行测试。但是如果使用了ActionContext来获取session数据,那么就不能这样运行了。因为ActionContext包装的都是Web的数据,在Java应用程序中运行的时候,没有Web的环境和响应的数据,因而会抛出空指针的异常。访问其它的Web对象的值也是与此类似的,你通过ActionContext去访问的都是包装后的Map。

1.3 访问方式

例如:若给出一个OGNL表达式 : "class.user..name"

(1)首先判断这个OGNL表达式前缀没有带#,所以这个一个根对象,struts2框架会在值栈的栈顶对象中查找getclass()方法,得到class对象,然后通过class对象,在其中查找getUer()方法,得到user对象,然后在user对象中查找getName())方法,得到最终的值!

(2)对于客户端发送的每一次请求,struts2框架都会自动的创建一个ActionContext和值栈对象,请求处理完毕以后这两个对象会自动的销毁!ActionContext对象相当于一个大大的容器,里面包含了application、session、parameters、valueSatck、attr属性。(其中valueStack对象就是值栈对象)

(3)ActionContext中的application、session、parameters等对象都是非根对象,在OGNL表达式中要访问他们,需要在表达式前面加上#前缀!

例如:

 ① #appliation.userName等于 ActionContext.getContext().getApplication().get("userName");

 ② #session.userName等效于ActionContext.getContext().getSession().get("userName");

 ③ #parameters.userName等ActionContext.getContext()..getParameters().get("userName");

1.4 ActionContex与ServletActionContext的关系

在实际应用开发中,光是获取数据就够了吗?答案显然是否定的,有些时候,根据功能需要,在Action中必须要能获取到Servlet相关的API,比如要操作Cookie。这个时候,就需要用ServletActionContext了。

  • ServletActionContext

这个类直接继承了ActionContext,当然也继承了它父类的很多功能,比如:对OgnlValueStack、Action名字等的访问。更重要的是,它还提供了直接访问Servlet的相关对象的功能,它可以取得的对象有:

HttpServletRequest:请求对象
HttpServletResponse:响应对象
ServletContext:Servlet上下文信息
PageContext:Http页面上下文
  • ServletActionContext使用方式
HttpServletRequest request = ServletActionContext.getRequest();  
HttpServletResponse response = ServletActionContext.getResponse();  
ServletContext servletContext = ServletActionContext.getServletContext();  
PageContext pageContext = ServletActionContext.getPageContext();  
HttpSession session = ServletActionContext.getRequest().getSession();  
  • ActionContext和ServletActionContext区别

  根据前面的讲述,你会发现,ActionContext和ServletActionContext有着一些重复的功能,都能够获取到Web对象的数据,但是又有些不同。通常情况下,可以这么认为:ActionContext主要负责值的操作;ServletActionContext主要负责获取Servlet对象。那么在Action中,该如何去抉择呢?建议的原则是:

  • 优先使用ActionContext
  • 只有ActionContext不能满足功能要求的时候,才使用ServletActionContext

  总之,要尽量让Action与Web无关,这对于Action的测试和复用都是极其有好处的。另外还有一点需要注意:在使用ActionContext时,不要在Action的构造函数里使用ActionContext.getContext(),因为这个时候ActionContext里的一些值也许还没有设置,这时通过ActionContext取得的值也许是null。

参考资料:http://www.iteye.com/topic/1124526

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值