《刨根问底--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对象后的截图: