值栈(ValueStack)
Struts2将OGNL上下文设置为Struts2中的ActionContext(内部使用的仍然是OgnlContext),并将值栈设为OGNL的根对象。
OGNL上下文中的根对象可以用ONGL表达式以对象名直接访问的,不需要使用任何特殊的“标记”,而引用上下文中的其他对象则需要使用“#”来标记。由于值栈是OGNL上下文中的根对象,因此可以直接访问。那么对于值栈中的对象该如何访问呢?Struts2提供了一个特殊的OGNLPropertyAccessor,它可以自动查找栈内的所有对象(从栈顶到栈底),直接找到一个具有你所查找的属性的对象。也就是说,对于值栈中的任何对象都可以直接访问,而不需要使用“#”。
值栈中的对象都是OGNL上下文中的根对象。这就是Struts2在OGNL基础上做出的改进。
ActionContext
ActionContext是Action的环境,一次Action调用都会创建一个ActionContext
调用:ActionContext context = ActionContext.getContext()
Struts2对OGNL上下文的概念又做了进一步扩充,在struts2中,OGNL上下文通常如下所示:
|--request
|
|--application
|
context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
|
|--session
|
|--attr
|
|--parameters
在Struts2中,采用标准命名的上下文(Context)来处理OGNL表达式。处理OGNL的顶级对象是一个Map(也叫context map),而OGNL在这个context中就是一个顶级对象(root)。在用法上,顶级对象的属性访问,是不需要任何标记前缀的。而其它非顶级的对象访问,需要使用#标记。
例如:
<s:property value="%{#application.myApplicationKey}" />
<s:property value="%{#session.mySessionKey}" />
<s:property value="%{#request.myRequestKey}" />
<s:property value="%{#parameters.myParameterKey}" />
Struts2框架把OGNL Context设置为ActionContext。并且ValueStack作为OGNL的根对象。除value stack之外,Struts2框架还把代表application、session、request这些对象的Map对象也放到ActionContext中去。(这也就是Struts2建议在Action类中不要直接访问Servlet API的原因,他可以通过ActionContext对象来部分代替这些(Servlet API)功能。)
Struts2把页面上的值传到Action里面,这个过程从逻辑上说需要分成两步来完成:
1. 对于每个请求,都建立一个与相应Action对应的ActionContext作为OGNL的上下文环境和ValueStack,并且把Action里面的属性值压入ValueStack,这时的属性值都是初始化值。
2. 在请求进入Action代码前,通过某种通用的机制(就是Struts2的interceptor拦截器机制),搜集页面上传递过来的参数,这里调用的是params拦截器,准确来说是这个拦截器会找Action里面的所有属性的set方法,把页面的Struts2标签的对应名称的属性值set进去,这样Action里面的属性就成功赋值。
设计Struts2的团队初衷就是为了不用和那些Servlet API复杂的请求(Request)、响应(Response)关联在一起。
Struts2的线程安全
一般情况,我们的ActionContext都是通过:ActionContext context = (ActionContext) actionContext.get();来获取的。我们再来看看这里的actionContext对象的创建:static ThreadLocal actionContext = new ActionContextThreadLocal();,ActionContextThreadLocal是实现ThreadLocal的一个内部类。ThreadLocal可以命名为“线程局部变量”,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。这样,我们ActionContext里的属性只会在对应的当前请求线程中可见,从而保证它是线程安全的。
Struts2的action中就像一个POJO一样,定义了很多的类变量。此时,就使用scope=prototype来指定是个原型模式,而不是单例,这样就解决了线程安全问题。每个线程都是一个新的实例。