SpEL support for methods and properties on class …

Update the ReflectiveMethodResolver and ReflectivePropertyAccessor
to allow methods and properties of java.lang.Class to be resolved
when the target object is a class.

Issue: SPR-9017
This commit is contained in:
Phillip Webb 2012-09-06 20:06:35 -07:00
parent 98808347ca
commit d28592a6c6
4 changed files with 67 additions and 14 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
@ -90,7 +92,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
try {
TypeConverter typeConverter = context.getTypeConverter();
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
Method[] methods = getMethods(type);
Method[] methods = getMethods(type, targetObject);
// If a filter is registered for this type, call it
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
@ -197,6 +199,16 @@ public class ReflectiveMethodResolver implements MethodResolver {
}
}
private Method[] getMethods(Class<?> type, Object targetObject) {
if(targetObject instanceof Class) {
Set<Method> methods = new HashSet<Method>();
methods.addAll(Arrays.asList(getMethods(type)));
methods.addAll(Arrays.asList(getMethods(targetObject.getClass())));
return methods.toArray(new Method[methods.size()]);
}
return getMethods(type);
}
/**
* Return the set of methods for this type. The default implementation returns the
* result of Class#getMethods for the given {@code type}, but subclasses may override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -71,7 +71,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (this.readerCache.containsKey(cacheKey)) {
return true;
}
Method method = findGetterForProperty(name, type, target instanceof Class);
Method method = findGetterForProperty(name, type, target);
if (method != null) {
// Treat it like a property
// The readerCache will only contain gettable properties (let's not worry about setters for now)
@ -82,7 +82,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return true;
}
else {
Field field = findField(name, type, target instanceof Class);
Field field = findField(name, type, target);
if (field != null) {
TypeDescriptor typeDescriptor = new TypeDescriptor(field);
this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor));
@ -112,7 +112,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (invoker == null || invoker.member instanceof Method) {
Method method = (Method) (invoker != null ? invoker.member : null);
if (method == null) {
method = findGetterForProperty(name, type, target instanceof Class);
method = findGetterForProperty(name, type, target);
if (method != null) {
// TODO remove the duplication here between canRead and read
// Treat it like a property
@ -138,7 +138,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (invoker == null || invoker.member instanceof Field) {
Field field = (Field) (invoker == null ? null : invoker.member);
if (field == null) {
field = findField(name, type, target instanceof Class);
field = findField(name, type, target);
if (field != null) {
invoker = new InvokerPair(field, new TypeDescriptor(field));
this.readerCache.put(cacheKey, invoker);
@ -168,7 +168,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (this.writerCache.containsKey(cacheKey)) {
return true;
}
Method method = findSetterForProperty(name, type, target instanceof Class);
Method method = findSetterForProperty(name, type, target);
if (method != null) {
// Treat it like a property
Property property = new Property(type, null, method);
@ -178,7 +178,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return true;
}
else {
Field field = findField(name, type, target instanceof Class);
Field field = findField(name, type, target);
if (field != null) {
this.writerCache.put(cacheKey, field);
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
@ -211,7 +211,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (cachedMember == null || cachedMember instanceof Method) {
Method method = (Method) cachedMember;
if (method == null) {
method = findSetterForProperty(name, type, target instanceof Class);
method = findSetterForProperty(name, type, target);
if (method != null) {
cachedMember = method;
this.writerCache.put(cacheKey, cachedMember);
@ -232,7 +232,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (cachedMember == null || cachedMember instanceof Field) {
Field field = (Field) cachedMember;
if (field == null) {
field = findField(name, type, target instanceof Class);
field = findField(name, type, target);
if (field != null) {
cachedMember = field;
this.writerCache.put(cacheKey, cachedMember);
@ -281,6 +281,30 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return typeDescriptor;
}
private Method findGetterForProperty(String propertyName, Class<?> clazz, Object target) {
Method method = findGetterForProperty(propertyName, clazz, target instanceof Class);
if(method == null && target instanceof Class) {
method = findGetterForProperty(propertyName, target.getClass(), false);
}
return method;
}
private Method findSetterForProperty(String propertyName, Class<?> clazz, Object target) {
Method method = findSetterForProperty(propertyName, clazz, target instanceof Class);
if(method == null && target instanceof Class) {
method = findSetterForProperty(propertyName, target.getClass(), false);
}
return method;
}
private Field findField(String name, Class<?> clazz, Object target) {
Field field = findField(name, clazz, target instanceof Class);
if(field == null && target instanceof Class) {
field = findField(name, target.getClass(), false);
}
return field;
}
/**
* Find a getter method for the specified property.
*/
@ -410,7 +434,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (invocationTarget == null || invocationTarget.member instanceof Method) {
Method method = (Method) (invocationTarget==null?null:invocationTarget.member);
if (method == null) {
method = findGetterForProperty(name, type, target instanceof Class);
method = findGetterForProperty(name, type, target);
if (method != null) {
invocationTarget = new InvokerPair(method,new TypeDescriptor(new MethodParameter(method,-1)));
ReflectionUtils.makeAccessible(method);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.expression.spel;
import static org.junit.Assert.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@ -361,4 +363,10 @@ public class MethodInvocationTests extends ExpressionTestCase {
evaluateAndCheckError("null.toString()",SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED);
}
@Test
public void testMethodOfClass() throws Exception {
Expression expression = parser.parseExpression("getName()");
Object value = expression.getValue(new StandardEvaluationContext(String.class));
assertEquals(value, "java.lang.String");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.expression.spel;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
@ -155,6 +157,13 @@ public class PropertyAccessTests extends ExpressionTestCase {
Assert.assertEquals(2,ctx.getPropertyAccessors().size());
}
@Test
public void testAccessingPropertyOfClass() throws Exception {
Expression expression = parser.parseExpression("name");
Object value = expression.getValue(new StandardEvaluationContext(String.class));
assertEquals(value, "java.lang.String");
}
// This can resolve the property 'flibbles' on any String (very useful...)
private static class StringyPropertyAccessor implements PropertyAccessor {