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