1. 程式人生 > >OgnlValueStack的結構和幾個方法分析

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中尋找,到此原始碼解讀完畢