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。