lection);
} else {
return getBeanProperty(prop, object);
}
}
protected Object resolveCollection(PropertyTokenizer prop, Object object) {
if ("".equals(prop.getName())) {
return object;
} else {
return metaObject.getValue(prop.getName());
}
}
protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
if (collection instanceof Map) {
// 如果是map,则直接取"i"对应的value
return ((Map) collection).get(prop.getIndex());
} else {
// 否则取集合或者数组中的对应值。下面一堆神奇的if else if是为啥,参考后记2
int i = Integer.parseInt(prop.getIndex());
if (collection instanceof List) {
return ((List) collection).get(i);
} else if (collection instanceof Object[]) {
return ((Object[]) collection)[i];
} else if (collection instanceof char[]) {
return ((char[]) collection)[i];
} else if (collection instanceof boolean[]) {
return ((boolean[]) collection)[i];
} else if (collection instanceof byte[]) {
return ((byte[]) collection)[i];
} else if (collection instanceof double[]) {
return ((double[]) collection)[i];
} else if (collection instanceof float[]) {
return ((float[]) collection)[i];
} else if (collection instanceof int[]) {
return ((int[]) collection)[i];
} else if (collection instanceof long[]) {
return ((long[]) collection)[i];
} else if (collection instanceof short[]) {
return ((short[]) collection)[i];
} else {
throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
}
}
}
private Object getBeanProperty(PropertyTokenizer prop, Object object) {
try {
// 反射获取getter方法。
Invoker method = metaClass.getGetInvoker(prop.getName());
try {
// 执行getter方法获取值
return method.invoke(object, NO_ARGUMENTS);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (RuntimeException e) {
throw e;
} catch (Throwable t) {
throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ". Cause: " + t.toString(), t);
}
}
至此,#{propertyName}的解析就完成了。${}则是直接使用的OGNL表达式解析,不详细解析了。
结论
下面回到问题,仔细分析后,得到错误原因:
上面第三步中,生成的ObjectWrapper类型是BeanWrapper,而BeanWrapper中获取属性值length,会调用反射尝试获取getter方法,并执行。对于一个数组类型的对象,当然是不可能有getter方法的(仅指java)。
而在test中的ids.length则没有问题,是因为test中的表达式是使用的OGNL来执行的。参考第一部分的Expressioneva luator。最后的则是执行的第二部分中的代码逻辑,故报错。
解决
解决方法有三种:
- 更换#{array.length}为${array.length}即可解决。
- 使用<bind />
<bind name="idCount" value="ids.length" />
LIMIT #{idCount}
读者可以尝试去看下bind标签的处理逻辑。 3. 如上面一样,增加ArrayWrapper:
public class ArrayWrapper implements ObjectWrapper {
private final Object object;
public ArrayWrapper(MetaObject metaObject, Object object) {
if (object.getClass().isArray()) {
this.object = object;
} else {
throw new IllegalArgumentException("object must be an array");
}
}
@Override
public Object get(PropertyTokenizer prop) {
if ("length".equals(prop.getName())) {
return Array.getLength(object);
}
throw new UnsupportedOperationException();
}
... // 其他未覆盖方法均抛出UnsupportedOperationException异常。
}
这里通过判断属性值为”length”来获取数组长度,其他均抛出异常。这样便支持了#{}占位符中数组长度的获取。
后记
- 有意思的注释
if (value.getClass().isArray()) {
// the array may b