您的位置:首页 > 产品设计 > UI/UE

OgnlValueStack的结构和几个方法分析

2016-09-26 10:30 417 查看
首先,我们来看一下在那里创建了OgnlValueStack。在Struts2过滤器的doFilter方法中 prepare.createActionContext(request, response)这一条代码中,创建了actioncontext,并且创建了valuestack。下面我们来看看。

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


走到createValueStack()那一条代码。

public ValueStack createValueStack() {
ValueStack stack = new OgnlValueStack(xworkConverter, compoundRootAccessor, textProvider, allowStaticMethodAccess);
container.inject(stack);
stack.getContext().put(ActionContext.CONTAINER, container);
return stack;
}


这里在valuestack的context中放入了ActionContext.CONTAINER

protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
push(prov);
}


这里看到将prov放到了valuestack的根栈中,而这个prov就是TextProvider

protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot,
boolean allowStaticMethodAccess) {
this.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), securityMemberAccess);
context.put(VALUE_STACK, this);
Ognl.setClassResolver(context, accessor);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
}


这里为valuestack设置一些属性值,比如根栈和context,这里我们去看看如何创建context。createDefaultContext这个方法就是创建了context。

public static Map createDefaultContext(Object root, ClassResolver classResolver,
TypeConverter converter, MemberAccess memberAccess)
{
return addDefaultContext(root, classResolver, converter, memberAccess, new OgnlContext());
}


继续去看一下addDefaultContext方法

public static Map addDefaultContext(Object root, ClassResolver classResolver,
TypeConverter converter, MemberAccess memberAccess, Map context)
{
OgnlContext result;

if (!(context instanceof OgnlContext)) {
result = new OgnlContext();
result.setValues(context);
} else {
result = (OgnlContext) context;
}
if (classResolver != null) {
result.setClassResolver(classResolver);
}
if (converter != null) {
result.setTypeConverter(converter);
}
if (memberAccess != null) {
result.setMemberAccess(memberAccess);
}

result.setRoot(root);
return result;
}


这里返回一个result,并且将root设置给result,而result对象是一个OgnlContext,实现了Map接口,而这个root是设置给result对象的_root对象,下面继续看一下这个方法

public void setRoot(Object value)
{
_root = value;
_accessorStack.clear();
_typeStack.clear();
_currentObject = value;

if (_currentObject != null)
{
setCurrentType(_currentObject.getClass());
}
}


下面我们可以知道valuestack的结构,引用别人的一张图,在此感谢图的作者。



下面稍微看一下OgnlValueStack几个方法

1、setValue(expr,value)方法

public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
Map<String, Object> context = getContext();
try {
trySetValue(expr, value, throwExceptionOnFailure, context);
} catch (OgnlException e) {
handleOgnlException(expr, value, throwExceptionOnFailure, e);
} catch (RuntimeException re) { //XW-281
handleRuntimeException(expr, value, throwExceptionOnFailure, re);
} finally {
cleanUpContext(context);
}
}


看一下trySetValue方法

private void trySetValue(String expr, Object value, boolean throwExceptionOnFailure, Map<String, Object> context) throws OgnlException {
context.put(XWorkConverter.CONVERSION_PROPERTY_FULLNAME, expr);
context.put(REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
ognlUtil.setValue(expr, context, root, value);
}


继续看ognlUtil.setValue方法

public void setValue(final String name, final Map<String, Object> context, final Object root, final Object value) throws OgnlException {
compileAndExecute(name, context, new OgnlTask<Void>() {
public Void execute(Object tree) throws OgnlException {
if (isEvalExpression(tree, context)) {
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
}
if (isArithmeticExpression(tree, context)) {
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
}
Ognl.setValue(tree, context, root, value);
return null;
}
});
}


这里去看一下compileAndExecute()方法

private <T> Object compileAndExecute(String expression, Map<String, Object> context, OgnlTask<T> task) throws OgnlException {
Object tree;
if (enableExpressionCache) {
tree = expressions.get(expression);
if (tree == null) {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
}
} else {
tree = Ognl.parseExpression(expression);
checkEnableEvalExpression(tree, context);
}

final T exec = task.execute(tree);
// if cache is enabled and it's a valid expression, puts it in
if(enableExpressionCache) {
expressions.putIfAbsent(expression, tree);
}
return exec;
}


这里发现是将expr转化成tree对象了

下面继续回到ognlUtil.setValue方法,找到这一条代码 Ognl.setValue()方法,去看一下啊

public static void setValue(Object tree, Map context, Object root, Object value)
throws OgnlException
{
OgnlContext ognlContext = (OgnlContext) addDefaultContext(root, context);
Node n = (Node) tree;

if (n.getAccessor() != null) {
n.getAccessor().set(ognlContext, root, value);
return;
}

n.setValue(ognlContext, root, value);
}


继续 n.setValue(ognlContext, root, value)

public final void setValue(OgnlContext context, Object target, Object value)
throws OgnlException
{
if (context.getTraceEvaluations())
{
EvaluationPool pool = OgnlRuntime.getEvaluationPool();
Throwable evalException = null;
Evaluation evaluation = pool.create(this, target, true);

context.pushEvaluation(evaluation);
try {
evaluateSetValueBody(context, target, value);
}
catch (OgnlException ex) {
evalException = ex;
ex.setEvaluation(evaluation);
throw ex;
}
catch (RuntimeException ex) {
evalException = ex;
throw ex;
} finally {
Evaluation eval = context.popEvaluation();

if (evalException != null) {
eval.setException(evalException);
}
if ((evalException == null) && (context.getRootEvaluation() == null)
&& !context.getKeepLastEvaluation()) {
pool.recycleAll(eval);
}
}
} else {
evaluateSetValueBody(context, target, value);
}
}


这里去看一下evaluateSetValueBody()方法

protected void evaluateSetValueBody(OgnlContext context, Object target, Object value)
throws OgnlException
{
context.setCurrentObject(target);
context.setCurrentNode(this);
setValueBody(context, target, value);
}


看setValueBody方法

protected void setValueBody(OgnlContext context, Object target, Object value)
throws OgnlException
{
throw new InappropriateExpressionException(this);
}


在这个类没有实现,在其他子类中实现,也就是说转化的tree其实不是simplenNode对象,而是跟对象的子类。这里大体就是如果不带#就放到valuestack的根栈中,如果带#就放到context中。

2、set(key,value)

public void set(String key, Object o) {
//set basically is backed by a Map pushed on the stack with a key being put on the map and the Object being the value
Map setMap = retrieveSetMap();
setMap.put(key, o);
}


看一下retrieveSetMap()f方法

private Map retrieveSetMap() {
Map setMap;
Object topObj = peek();
if (shouldUseOldMap(topObj)) {
setMap = (Map) topObj;
} else {
setMap = new HashMap();
setMap.put(MAP_IDENTIFIER_KEY, "");
push(setMap);
}
return setMap;
}


这里的意思就是先在根栈中寻找栈顶是否是map类型的对象,如果是则使用,如果不是就新创建一个map,然后将新建好的map放入栈顶。然后看到一开始的set方法内,将key,value设置给map。

3、findValue,findString方法

public String findString(String expr) {
return (String) findValue(expr, String.class);
}

public String findString(String expr, boolean throwExceptionOnFailure) {
return (String) findValue(expr, String.class, throwExceptionOnFailure);
}


这里是全都调用findValue方法

public Object findValue(String expr, boolean throwExceptionOnFailure) {
try {
setupExceptionOnFailure(throwExceptionOnFailure);
return tryFindValueWhenExpressionIsNotNull(expr);
} catch (OgnlException e) {
return handleOgnlException(expr, throwExceptionOnFailure, e);
} catch (Exception e) {
return handleOtherException(expr, throwExceptionOnFailure, e);
} finally {
ReflectionContextState.clear(context);
}
}


public Object findValue(String expr) {
return findValue(expr, false);
}


public Object findValue(String expr, Class asType, boolean throwExceptionOnFailure) {
try {
setupExceptionOnFailure(throwExceptionOnFailure);
return tryFindValueWhenExpressionIsNotNull(expr, asType);
} catch (OgnlException e) {
final Object value = handleOgnlException(expr, throwExceptionOnFailure, e);
return converter.convertValue(getContext(), value, asType);
} catch (Exception e) {
final Object value = handleOtherException(expr, throwExceptionOnFailure, e);
return converter.convertValue(getContext(), value, asType);
} finally {
ReflectionContextState.clear(context);
}
}


发现这几个findValue方法都调用了tryFindValueWhenExpressionIsNotNull()。走进去看一下

private Object tryFindValueWhenExpressionIsNotNull(String expr, Class asType) throws OgnlException {
if (expr == null) {
return null;
}
return tryFindValue(expr, asType);
}


看tryFindValue()方法

private Object tryFindValue(String expr, Class asType) throws OgnlException {
Object value = null;
try {
expr = lookupForOverrides(expr);
value = getValue(expr, asType);
if (value == null) {
value = findInContext(expr);
return converter.convertValue(getContext(), value, asType);
}
} finally {
context.remove(THROW_EXCEPTION_ON_FAILURE);
}
return value;
}


在这里看到看到getValue和findInContext这两个方法

private Object getValue(String expr, Class asType) throws OgnlException {
return ognlUtil.getValue(expr, context, root, asType);
}

private Object findInContext(String name) {
return getContext().get(name);
}


可以看出先是到valuestack的根栈中寻找,然后到context中寻找,到此源码解读完毕
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  struts ongl valuestack