/**
* The struts framework sets the OGNL context to be our ActionContext
* and the value stack to be the OGNL root object.
* (The value stack is a set of several objects
* but to OGNL it appears to be a single object.)
*
@author
LISAI
*
*/
public
class
Ognl {
/**
* 通过传入OGNL表达式,在给定的上下文环境.从root对象中取值.
* OGNL不是Struts独有的.解决的问题是:web层弱类型和Java强类型的之间的转换.表达式引擎.
*
@param
expression
*
@param
context
-------
ActionContext
*
@param
root
------
ValueStack (虽然是数据结构是栈.A Stack that is implemented using a List)
*
@return
Object
*/
public
static
Object getValue(
String
expression,
Map
context,Object root){
return
null
;
}
/**
* 通过传入OGNL表达式,在给定的上下文环境.向root对象中写值.
*
@param
expression
*
@param
context
*
@param
root
*
@return
Object
*/
public
static
void
setValue(
String
expression,
Map
context,Object root){
}
}
|
通过上述代码可以发现:OGNL引擎主要干两件事情.取值和存值.而这些操作都必须传入三个参数.以后无论OGNL如何完成复杂的操作,都会映射到这三个参数,通过调用底层引擎完成相关计算.从而完成OGNL功能需要三个要素: a.表达式(Expression),表达式会规定此次OGNL操作到底要干什么. b.Root对象.可以理解为OGNL的操作对象.也就是"对谁干",Root对象实际上就是一个Java对象,在struts2中相对应的就是实现ValueStack接口的类OgnlValueStack对象中CompoundRoot root对象.(CompoundRoot类是一个有Java List集合构建的栈的结构,存放一组对象.下面在详细说).The value stack is a set of several objects but to OGNL it appears to be a single object. c.上下文环境(OgnlContext).有了表达式和Root对象.就已经可以使用OGNL的基本功能.不过,事实上在OGNL内部,所有的操作都会在一个特定的数据环境中运行,这个数据环境就是OGNL的上下文环境(Context)。说的明白一些,就是这个上下文环境规定OGNL的操作"在哪里干",OGNL的上下文环境就是Map结构.之前所提及的Root对象,也会被添加上下文环境中,并且被作为一个特殊的变量进行处理.在Struts2中就是ActionContext类实现的. |
public class ActionContext implements Serializable {
//通过ThreadLocal设计模式,实现ActionContext一次访问中的数据共享.线程安全 static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();
/**
* Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}. */ public static final String VALUE_STACK = ValueStack.VALUE_STACK; // ActionContext中存储数据的上下文环境,ValueStack就存储其中,也有个名字叫做OgnlContext
private Map<String, Object> context;
/** * Stores a value in the current ActionContext. The value can be looked up using the key. * @param key the key of the value. * @param value the value to be stored. */ public void put(String key, Object value) { context.put(key, value); } /** * Returns a value that is stored in the current ActionContext by doing a lookup using the value's key. * * @param key the key used to find the value. * @return the value that was found using the key or <tt>null</tt> if the key was not found. */ public Object get(String key) { return context.get(key); } /** * Sets the OGNL value stack. * 设置ValueStack * @param stack the OGNL value stack. */ public void setValueStack(ValueStack stack) { put(VALUE_STACK, stack); } /** * Gets the OGNL value stack. * 获取ValueStack * @return the OGNL value stack. */ public ValueStack getValueStack() { return (ValueStack) get(VALUE_STACK); } } |
从上面源码可以得出:
1.ActionContext
ActionContext是XWork中最重要的概念之一.它提供整个Xwork时间处理的上下文环境,在这个数据环境包含了所有事件处理过程中所需要的数据对象. 所以每一个事件响应,都有一个ActionContext对象的存在与之对应. 2.从源码中,我们可以看到ActionContext真正的数据存储空间,是位于其中Map类型的变量context.ActionContext将所有的数据对象以特定的key值存放在context之中.而ActionContext也提供获取重要元素的快捷方法:如getValueStack,getSession; 3.如何保证线程安全的:ThreadLocal设计模式 4.ActionContext所提供的数据的访问返回的都是Map类型的数据对象,而不是真正的HttpSession等这样纯正的Web容器对象.做到与Web容器无关. 好处:原生的web容器对象的本身不是线程安全的.而通过Xwork的封装。彻底消除这一隐患. 保持所有存储对象的Map结构,可以统一数据访问方式.
5.当Struts2接受一个请求时.会迅速创建ActionContext对象,ValueStack对象,action对象,然后把action实例对象(UserAction{name,password})存放进ValueStack,所以action的实例变量可以被OGNL访问.
6.ActionContext类中成员变量Map<String,Object> context的结构图:
如果需要访问上下文的对象需要#符号标注命名空间,如#application,#session,#request.注意:需要配合struts2的标签一起使用.
另外的话OGNL会设定一个根对象(root对象),在struts2中就是OgnlVauleStack(值栈,是一个list集合).如果要访问根对象中对象的属性.则可以忽略#命名空间,直接访问该对象的属性即可.如如果UserAction对象(存在name属性)在值栈中的话,就可以直接<s:property value="name">.至于值栈是什么样的查询规则而又为什么不需要#命名空间?.下面详解:
|
ValueStatck是针对OGNL计算的扩展,实际上就是针对OGNL三要素中Root对象所进行的扩展.使得在Struts2在使用ValueStack进行OGNL、计算时,可以将一组对象都视为Root对象.
|
OgnlValueStack类的源代码: |
public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {
//使用装饰模式,将根元素(栈结构)封装在ValueStack对象内容. //从外界来看,所有的操作就像针对单一对象的操作.实际在内部,是List形成的栈结构. CompoundRoot root;
//OnglContext对象
transient
Map<String, Object>
context
;
//构造函数,这里讲制定OGNL计算所需的参数
protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticAccess) { //调用setRoot方法完成初始化 setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticAccess); } //真正的OgnlValueStack的初始化过程. protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) { //根对象是一个CompoundRoot类型的栈结构 this.root = compoundRoot; this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess); //创建OGNL的上下文 this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), securityMemberAccess); //设置OGNL上下文的其他相关参数 context.put(VALUE_STACK, this); Ognl.setClassResolver(context, accessor); ((OgnlContext) context).setTraceEvaluations(false); ((OgnlContext) context).setKeepLastEvaluation(false); } } |
OgnlValueStack使用了一个典型的装饰模式,真正的其内部起到核心作用的是一个叫做CompoundRoot root的数据结构,可以存储多个对象(栈结构).ValueStack是一个被设计成"栈"的数据结构.并且还是一个具备表达式引擎计算能力的数据结构.OGNL与Stack的结合.
|
/**
* A Stack that is implemented using a List. * @author plightbo * @version $Revision: 894090 $ */ public class CompoundRoot extends ArrayList { public CompoundRoot() { } public CompoundRoot(List list) { super(list); } public CompoundRoot cutStack(int index) { return new CompoundRoot(subList(index, size())); } public Object peek() { return get(0); } public Object pop() { return remove(0); } public void push(Object o) { add(0, o); } } |