刨根问底--ognl--get获取数据

刨根问底--action属性赋值过程分析》概要的分析了action赋值的需要通过ognl,由于前面的文章中没有分析过ognl,如果上来直接分析struts2是怎么使用,嘿嘿,相信没有看过ognl的人,直接晕掉。这篇文章就先舍去struts2,单独来分析ognl。

测试用例:

import ognl.Ognl;
import ognl.OgnlException;

public class OgnlTest  
{  
    public static void main(String[] args)  
    {  
        /* 创建一个Person对象 */ 
        Person person = new Person();  
        person.setName("zhangsan");  
          
        try 
        {  
            /* 从person对象中获取name属性的值 */ 
        	//parseExpression(expression)
//        	Ognl.setValue(expression, person, "zhangsan");
        	String expression = "name"; 
            Object value = Ognl.getValue(expression, person);  
 
            System.out.println(value);  
        }  
        catch (OgnlException e)  
        {  
            e.printStackTrace();  
        }  
    }  
}  
 
class Person  
{  
    private String name;  
 
    public String getName()  
    {  
        return name;  
    }  
 
    public void setName(String name)  
    {  
        this.name = name;  
    }  
}

注释:这个类中简单的几行代码包含了ognl获取数据的代码和设置数据的代码。现在重点分析获取数据,设置数据后面的文章分析。

(1)创建了一个Person对象,并且设置属性name为zhangsan

(2)Ognl.getValue()获取person对象中name对应的值

下面贴出整个流程图:

1、Ognl.getValue(expression, person)代码:

public static Object getValue( String expression, Object root ) throws OgnlException
    {
        return getValue( expression, root, null );
    }
注释:方法很简单,增加一个参数,并且调用getvalue方法。


2、getValue( expression, root, null )代码:

public static Object getValue( String expression, Object root, Class resultType ) throws OgnlException
    {
        return getValue( parseExpression(expression), root, resultType );
    }
注释:解析表达式 expression, parseExpression(expression)代码:


public static Object parseExpression( String expression ) throws OgnlException
    {
        try {
            OgnlParser parser = new OgnlParser( new StringReader(expression) );
            return parser.topLevelExpression();
        }
       。。。
    }
解析过程这里不详细的说明了,有兴趣的可以自行研究,解析完成后返回顶级表达式,在这里返回的是name


3、getValue( parseExpression(expression), root, resultType )代码:

public static Object getValue( Object tree, Object root, Class resultType ) throws OgnlException
    {
        return getValue( tree, createDefaultContext(root), root, resultType );
    }
注释:这里创建ognl的上下文环境,


3.1createDefaultContext(root)代码:

public static Map createDefaultContext( Object root )
    {
        return addDefaultContext( root, null, null, null, new OgnlContext() );
    }
注释:没有什么好说的,增加了4个参数,3个是null,new OgnlContext()对象


3.2 addDefaultContext( root, null, null, null, new OgnlContext() )代码:

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;
    }
注释:(1)首先判断context 是否是OgnlContext类型,如果不是则result = new OgnlContext();,并且设置values,如果是则 result = (OgnlContext)context。由于步骤3.1  new OgnlContext()对象,然后传进来的,所以 result = (OgnlContext)context。

(2)把root设置到result 中,root是person对象。

到这里其实复杂的逻辑没有,但是很重要,到这里,前面的几步主要是准备了ognl的三要素:

 (1).expression 求值表达式——首先会被解析成对象树:tree

 (2).rootobject  根对象——默认的操作对象:root

 (3).context OGNL执行环境——OGNL执行的上下文环境:createDefaultContext(root)

4、getValue( tree, createDefaultContext(root), root, resultType ) 代码:

public static Object getValue( Object tree, Map context, Object root, Class resultType ) throws OgnlException
    {
        Object          result;
        OgnlContext     ognlContext = (OgnlContext)addDefaultContext(root, context);

        result = ((Node)tree).getValue( ognlContext, root );
        if (resultType != null) {
            result = getTypeConverter( context ).convertValue( context, root, null, null, result, resultType);
        }
        return result;
    }

注释:(1)(OgnlContext)addDefaultContext(root, context)获取ognl上下文,请参照步骤3.2

(3)tree是SimpleNode类型,根据ognl上下文和root对象,获取值

5、SimpleNode.java --getValue()代码:

public final Object getValue( OgnlContext context, Object source ) throws OgnlException
    {
        if (context.getTraceEvaluations()) {
            EvaluationPool  pool = OgnlRuntime.getEvaluationPool();
            Object          result = null;
            Throwable       evalException = null;
            Evaluation      evaluation = pool.create(this, source);

            context.pushEvaluation(evaluation);
            try {
                result = evaluateGetValueBody(context, source);
            } catch (OgnlException ex) {
                evalException = ex;
                throw ex;
            } catch (RuntimeException ex) {
                evalException = ex;
                throw ex;
            } finally {
                Evaluation      eval = context.popEvaluation();

                eval.setResult(result);
                if (evalException != null) {
                    eval.setException(evalException);
                }
                if ((evalException == null) && (context.getRootEvaluation() == null) && !context.getKeepLastEvaluation()) {
                    pool.recycleAll(eval);
                }
            }
            return result;
        } else {
            return evaluateGetValueBody(context, source);
        }
    }
注释:context.getTraceEvaluations()创建ognl上下文时候traceEvaluations= false;在上面的过程中没有设置 traceEvaluations,所以获取的值为false。

那就直接看evaluateGetValueBody(context, source)方法:

6、evaluateGetValueBody(context, source)代码:

protected Object evaluateGetValueBody( OgnlContext context, Object source ) throws OgnlException
    {
        Object      result;

        context.setCurrentObject(source);
        context.setCurrentNode(this);
        if (!constantValueCalculated) {
            constantValueCalculated = true;
            hasConstantValue = isConstant(context);
            if (hasConstantValue) {
                constantValue = getValueBody(context, source);
            }
        }
        return hasConstantValue ? constantValue : getValueBody(context, source);
    }
注释:(1)设置ognl上下文的当前对象(这里是person)和当前节点的值

(2)constantValueCalculated表示当前的节点是否计算过值,如果没有计算过,则设置constantValueCalculated=true,hasConstantValue表示当前的节点是否保存了值,isConstant(context)简单的反回了fanse。

public boolean isConstant( OgnlContext context ) throws OgnlException
    {
        return isNodeConstant(context);
    }

 /**
        Returns true iff this node is constant without respect to the children.
     */
    public boolean isNodeConstant( OgnlContext context ) throws OgnlException
    {
        return false;
    }
(3)最重要的代码ASTProperty中的getValueBody,获取值。

7、ASTProperty.java中getValueBody()代码:

protected Object getValueBody( OgnlContext context, Object source ) throws OgnlException
    {
        Object      result,
                    property = getProperty(context, source);
        Node        indexSibling;

        result = OgnlRuntime.getProperty( context, source, property );
        if (result == null) {
            result = OgnlRuntime.getNullHandler(OgnlRuntime.getTargetClass(source)).nullPropertyValue(context, source, property);
        }
        return result;
    }
注释:(1) getProperty(context, source)获取属性值,这里返回的是name

(2)OgnlRuntime.getProperty( context, source, property )获取和属性property想对应的值

8、OgnlRuntime.getProperty( context, source, property )代码:

public static final Object getProperty( OgnlContext context, Object source, Object name ) throws OgnlException
    {
        PropertyAccessor        accessor;

        if (source == null) {
            throw new OgnlException("source is null for getProperty(null, \"" + name + "\")");
        }
        if ((accessor = getPropertyAccessor(getTargetClass(source))) == null) {
            throw new OgnlException("No property accessor for " + getTargetClass(source).getName());
        }
        return accessor.getProperty( context, source, name );
    }
注释:(1)判断source是否为空,如果为空直接抛异常,因为后面需要在 source对象中获取一些信息,所以不能为空

(2)accessor = getPropertyAccessor(getTargetClass(source))获取属性访问器对象,然后调用属性访问器的getProperty方法获取值。先看看属性访问器是怎么获得?

8.1getTargetClass(source)代码:

public static Class getTargetClass(Object o)
    {
        return (o == null) ? null : ((o instanceof Class) ? (Class)o : o.getClass());
    }

注释:获取target所对应的class,返回结果是:class Person

8.2getPropertyAccessor(getTargetClass(source)代码:

  private static ClassCache               propertyAccessors = new ClassCache();
public static final PropertyAccessor getPropertyAccessor( Class cls ) throws OgnlException
    {
        PropertyAccessor answer = (PropertyAccessor)getHandler( cls, propertyAccessors );
        if ( answer != null )
            return answer;

        throw new OgnlException( "No property accessor for class " + cls );
    }
 private static final Object getHandler( Class forClass, ClassCache handlers )
    {
        Object answer = null;


        synchronized(handlers) {
            if ((answer = handlers.get(forClass)) == null)
            {
                Class   keyFound;


                if (forClass.isArray())
                {
                    answer = handlers.get(Object[].class);
                    keyFound = null;
                }
                else
                {
                    keyFound = forClass;
                    outer:
                        for ( Class c = forClass; c != null; c = c.getSuperclass() )
                        {
                            answer = handlers.get(c);
                            if ( answer == null )
                            {
                                Class[] interfaces = c.getInterfaces();
                                for ( int index=0, count=interfaces.length; index < count; ++index )
                                {
                                    Class   iface = interfaces[index];


                                    answer = handlers.get(iface);
                                    if (answer == null)
                                    {
                                        /* Try super-interfaces */
                                        answer = getHandler(iface, handlers);
                                    }
                                    if ( answer != null )
                                    {
                                        keyFound = iface;
                                        break outer;
                                    }
                                }
                            }
                            else
                            {
                                keyFound = c;
                                break;
                            }
                        }
                }
                if ( answer != null )
                {
                    if ( keyFound != forClass )
                    {
                        handlers.put( forClass, answer );
                    }
                }
            }
        }
        return answer;
    }

 

  

注释:(1)getPropertyAccessor()方法调用getHandler( cls, propertyAccessors ),参数cls就是上面获取的class Person,propertyAccessors是OgnlRuntime类声明的静态变量。

(2)getHandler()方法首先在handlers查找时候有和class Person对象的属性访问器,如果有直接返回。

(3)重要的代码是for循环,首先查看当前的class在handlers是否有相应的值,如果没有获取当前calss的实现的接口,在查看这些接口是否有相应的值。

如果上面都没有找到,获取父类对象,然后再次遍历。

参数cls就是上面获取的class Person,没有实现别的接口,也没有继承其他的类。那属性访问器是怎么获得呢?

所有的类都会继承object,handlers保存了和object相对的属性访问器了。那这个属性访问器是什么时候添加进来的呢?

请看OgnlRuntime类中的静态方法

static
    {
        PropertyAccessor p = new ArrayPropertyAccessor();
        setPropertyAccessor( Object.class, new ObjectPropertyAccessor() );
        setPropertyAccessor( byte[].class, p );
        setPropertyAccessor( short[].class, p );
        setPropertyAccessor( char[].class, p );
        setPropertyAccessor( int[].class, p );
        setPropertyAccessor( long[].class, p );
        setPropertyAccessor( float[].class, p );
        setPropertyAccessor( double[].class, p );
        setPropertyAccessor( Object[].class, p );
        setPropertyAccessor( List.class, new ListPropertyAccessor() );
        setPropertyAccessor( Map.class, new MapPropertyAccessor() );
        setPropertyAccessor( Set.class, new SetPropertyAccessor() );
        setPropertyAccessor( Iterator.class, new IteratorPropertyAccessor() );
        setPropertyAccessor( Enumeration.class, new EnumerationPropertyAccessor() );
。。。。
    }
到这里,就获得了属性访问器,继续往下看,属性访问器是怎么获取值的。

9、ObjectPropertyAccessor类中getProperty( context, source, name )代码:

public Object getProperty( Map context, Object target, Object oname ) throws OgnlException
    {
        Object              result = null;
        String              name = oname.toString();

        if ((result = getPossibleProperty(context, target, name)) == OgnlRuntime.NotFound) {
            throw new NoSuchPropertyException(target, name);
        }
        return result;
    }
 public Object getPossibleProperty( Map context, Object target, String name) throws OgnlException
    {
        Object          result;
        OgnlContext     ognlContext = (OgnlContext)context;


        try {
            if ((result = OgnlRuntime.getMethodValue(ognlContext, target, name, true)) == OgnlRuntime.NotFound) {
                result = OgnlRuntime.getFieldValue(ognlContext, target, name, true);
            }
        } catch (IntrospectionException ex) {
            throw new OgnlException(name, ex);
        } catch (OgnlException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new OgnlException(name, ex);
        }
        return result;
    }
注释:上面的代码很简单首先OgnlRuntime.getMethodValue(ognlContext, target, name, true)获取值,如果没有找到,再去 OgnlRuntime.getFieldValue(ognlContext, target, name, true)获取值。

10、重点分析一下OgnlRuntime.getMethodValue(ognlContext, target, name, true)

public static final Object getMethodValue(OgnlContext context, Object target, String propertyName, boolean checkAccessAndExistence) throws OgnlException, IllegalAccessException, NoSuchMethodException, IntrospectionException
    {
        Object              result = null;
        Method              m = getGetMethod(context, (target == null) ? null : target.getClass(), propertyName);

        if (checkAccessAndExistence) {
            if ((m == null) || !context.getMemberAccess().isAccessible(context, target, m, propertyName)) {
                result = NotFound;
            }
        }
        if (result == null) {
            if (m != null)
            {
                try
                {
                    result = invokeMethod(target, m, NoArguments);
                }
                catch (InvocationTargetException ex)
                {
                    throw new OgnlException(propertyName, ex.getTargetException());
                }
            } else {
                throw new NoSuchMethodException(propertyName);
            }
        }
        return result;
    }
注释:(1)通过getGetMethod获取和name属性相对应的get方法。

(2)如果获取的get方法不为空,则通过java反射执行该方法。也就是调用了person中的getName()方法获取值,其实到这里就分析完成了。后面分析一下getGetMethod是怎么获取相应的方法的。

11、getGetMethod()代码:

public static final Method getGetMethod(OgnlContext context, Class targetClass, String propertyName) throws IntrospectionException, OgnlException
    {
        Method              result = null;
        PropertyDescriptor  pd = getPropertyDescriptor(targetClass, propertyName);

        if (pd == null) {
            List        methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */);

            if (methods != null) {
                for (int i = 0, icount = methods.size(); i < icount; i++) {
                    Method      m = (Method)methods.get(i);
                    Class[]     mParameterTypes = getParameterTypes(m);

                    if (mParameterTypes.length == 0) {
                        result = m;
                        break;
                    }
                }
            }
        } else {
            result = pd.getReadMethod();
        }
        return result;
    }

注释:(1)getPropertyDescriptor获取和属性name相关的方法,并且保存到PropertyDescriptor对象中。

PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:

<1、getPropertyType(),获得属性的Class对象。

<2、getReadMethod(),获得用于读取属性值的方法;getWriteMethod(),获得用于写入属性值的方法。

<3、hashCode(),获取对象的哈希值。

<4、setReadMethod(Method readMethod),设置用于读取属性值的方法;setWriteMethod(MethodwriteMethod),设置用于写入属性值的方法;

(2)PropertyDescriptor获取对象后,如果不为空则调用pd.getReadMethod()方法:

public synchronized Method getReadMethod() {
	Method readMethod = getReadMethod0();
	if (readMethod == null) {
	    Class cls = getClass0();
	    if (cls == null || (readMethodName == null && readMethodRef == null)) {
		// The read method was explicitly set to null.
		return null;
	    }
	    if (readMethodName == null) {
		Class type = getPropertyType0();
		if (type == boolean.class || type == null) {
		    readMethodName = "is" + getBaseName();
		} else {
		    readMethodName = "get" + getBaseName();
		}
	    }
	    
	    // Since there can be multiple write methods but only one getter
	    // method, find the getter method first so that you know what the
	    // property type is.  For booleans, there can be "is" and "get"
	    // methods.  If an "is" method exists, this is the official
	    // reader method so look for this one first.
	    readMethod = Introspector.findMethod(cls, readMethodName, 0);
	    if (readMethod == null) {
		readMethodName = "get" + getBaseName();
		readMethod = Introspector.findMethod(cls, readMethodName, 0);
	    }
	    try {
		setReadMethod(readMethod);
	    } catch (IntrospectionException ex) {
		// fall
	    }
	}
	return readMethod;
    }
   private Method getReadMethod0() {
 return (Method)getObject(readMethodRef);
    }
readMethodRef对象是在获取 PropertyDescriptor对象的时候就设置值,请看获取 PropertyDescriptor对象后的截图:
















转载于:https://my.oschina.net/winHerson/blog/109760

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值