OgnlValueStack的結構和幾個方法分析
首先,我們來看一下在那裡建立了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中尋找,到此原始碼解讀完畢