revised BeanTypeDescriptor into core PropertyTypeDescriptor; consider method annotations for return type (SPR-6979)

This commit is contained in:
Juergen Hoeller
2010-03-24 16:27:33 +00:00
parent 461c200ce6
commit 65b0a8fcb2
6 changed files with 49 additions and 142 deletions

View File

@@ -1,108 +0,0 @@
/*
* Copyright 2002-2009 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.support;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ReflectionUtils;
/**
* {@link TypeDescriptor} extension that exposes additional annotations as
* conversion metadata: namely, annotations on other accessor methods
* (getter/setter) and on the underlying field, if found.
*
* org.springframework.beans.BeanTypeDescriptor (beans module) is very
* similar to this but depending on that would introduce a beans
* dependency from the SpEL module.
*
* @author Juergen Hoeller
* @author Andy Clement
* @since 3.0
*/
public class BeanTypeDescriptor extends TypeDescriptor {
private final PropertyDescriptor propertyDescriptor;
private Annotation[] cachedAnnotations;
/**
* Create a new BeanTypeDescriptor for the given bean property.
*
* @param propertyDescriptor
* the corresponding JavaBean PropertyDescriptor
* @param methodParameter
* the target method parameter
* @param type
* the specific type to expose (may be an array/collection
* element)
*/
public BeanTypeDescriptor(PropertyDescriptor propertyDescriptor,
MethodParameter methodParameter, Class type) {
super(methodParameter, type);
this.propertyDescriptor = propertyDescriptor;
}
/**
* Return the underlying PropertyDescriptor.
*/
public PropertyDescriptor getPropertyDescriptor() {
return this.propertyDescriptor;
}
@Override
public Annotation[] getAnnotations() {
Annotation[] anns = this.cachedAnnotations;
if (anns == null) {
Field underlyingField = ReflectionUtils.findField(
getMethodParameter().getMethod().getDeclaringClass(),
this.propertyDescriptor.getName());
Map<Class, Annotation> annMap = new LinkedHashMap<Class, Annotation>();
if (underlyingField != null) {
for (Annotation ann : underlyingField.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method targetMethod = getMethodParameter().getMethod();
Method writeMethod = this.propertyDescriptor.getWriteMethod();
Method readMethod = this.propertyDescriptor.getReadMethod();
if (writeMethod != null && writeMethod != targetMethod) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
if (readMethod != null && readMethod != targetMethod) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
for (Annotation ann : targetMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
anns = annMap.values().toArray(new Annotation[annMap.size()]);
this.cachedAnnotations = anns;
}
return anns;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.PropertyTypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
@@ -80,17 +81,18 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
Method method = findGetterForProperty(name, type, target instanceof Class);
if (method != null) {
// Treat it like a property
PropertyDescriptor propertyDescriptor = null;
try {
// The readerCache will only contain gettable properties (let's not worry about setters for now)
propertyDescriptor = new PropertyDescriptor(name,method,null);
} catch (IntrospectionException ex) {
throw new AccessException("Unable to access property '" + name + "' through getter "+method, ex);
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null);
TypeDescriptor typeDescriptor =
new PropertyTypeDescriptor(propertyDescriptor, new MethodParameter(method, -1));
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
return true;
}
catch (IntrospectionException ex) {
throw new AccessException("Unable to access property '" + name + "' through getter " + method, ex);
}
TypeDescriptor typeDescriptor = new BeanTypeDescriptor(propertyDescriptor, new MethodParameter(method,-1), method.getReturnType());
this.readerCache.put(cacheKey, new InvokerPair(method,typeDescriptor));
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
return true;
}
else {
Field field = findField(name, type, target instanceof Class);
@@ -130,16 +132,18 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (method != null) {
// TODO remove the duplication here between canRead and read
// Treat it like a property
PropertyDescriptor propertyDescriptor = null;
try {
// The readerCache will only contain gettable properties (let's not worry about setters for now)
propertyDescriptor = new PropertyDescriptor(name,method,null);
} catch (IntrospectionException ex) {
throw new AccessException("Unable to access property '" + name + "' through getter "+method, ex);
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, method, null);
TypeDescriptor typeDescriptor =
new PropertyTypeDescriptor(propertyDescriptor, new MethodParameter(method, -1));
invoker = new InvokerPair(method, typeDescriptor);
this.readerCache.put(cacheKey, invoker);
}
catch (IntrospectionException ex) {
throw new AccessException(
"Unable to access property '" + name + "' through getter " + method, ex);
}
TypeDescriptor typeDescriptor = new BeanTypeDescriptor(propertyDescriptor, new MethodParameter(method,-1), method.getReturnType());
invoker = new InvokerPair(method,typeDescriptor);
this.readerCache.put(cacheKey, invoker);
}
}
if (method != null) {
@@ -201,7 +205,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
throw new AccessException("Unable to access property '" + name + "' through setter "+method, ex);
}
MethodParameter mp = new MethodParameter(method,0);
TypeDescriptor typeDescriptor = new BeanTypeDescriptor(propertyDescriptor,mp,mp.getParameterType());
TypeDescriptor typeDescriptor = new PropertyTypeDescriptor(propertyDescriptor, mp);
this.writerCache.put(cacheKey, method);
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
return true;