From 74705a7aea47e80968c674ddcf20c93d75f5ae05 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Mar 2010 16:27:33 +0000 Subject: [PATCH] revised BeanTypeDescriptor into core PropertyTypeDescriptor; consider method annotations for return type (SPR-6979) git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3162 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../beans/BeanWrapperImpl.java | 5 +- .../beans/TypeConverterDelegate.java | 7 +- .../core/convert/TypeDescriptor.java | 9 +- .../support/PropertyTypeDescriptor.java | 24 ++-- .../spel/support/BeanTypeDescriptor.java | 108 ------------------ .../support/ReflectivePropertyAccessor.java | 38 +++--- 6 files changed, 49 insertions(+), 142 deletions(-) rename org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java => org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java (76%) delete mode 100644 org.springframework.expression/src/main/java/org/springframework/expression/spel/support/BeanTypeDescriptor.java diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index c11a97acdd8..56753956ff4 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -44,6 +44,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.PropertyTypeDescriptor; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -368,10 +369,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra if (pd != null) { Class type = getPropertyType(propertyName); if (pd.getReadMethod() != null) { - return new BeanTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), type); + return new PropertyTypeDescriptor(pd, new MethodParameter(pd.getReadMethod(), -1), type); } else if (pd.getWriteMethod() != null) { - return new BeanTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd), type); + return new PropertyTypeDescriptor(pd, BeanUtils.getWriteMethodParameter(pd), type); } } } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index f966b3b5a11..231a3a7781f 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -33,6 +33,7 @@ import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.PropertyTypeDescriptor; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -152,7 +153,7 @@ class TypeConverterDelegate { throws IllegalArgumentException { return convertIfNecessary(descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), - new BeanTypeDescriptor(descriptor)); + new PropertyTypeDescriptor(descriptor, BeanUtils.getWriteMethodParameter(descriptor))); } /** @@ -348,8 +349,8 @@ class TypeConverterDelegate { */ protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) { PropertyEditor editor = null; - if (typeDescriptor instanceof BeanTypeDescriptor) { - PropertyDescriptor pd = ((BeanTypeDescriptor) typeDescriptor).getPropertyDescriptor(); + if (typeDescriptor instanceof PropertyTypeDescriptor) { + PropertyDescriptor pd = ((PropertyTypeDescriptor) typeDescriptor).getPropertyDescriptor(); editor = pd.createPropertyEditor(this.targetObject); } if (editor == null && requiredType != null) { diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 0e1f71a55f7..2ac6c7cd1ff 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -345,7 +345,14 @@ public class TypeDescriptor { return this.cachedFieldAnnotations; } else if (this.methodParameter != null) { - return this.methodParameter.getParameterAnnotations(); + if (this.methodParameter.getParameterIndex() < 0) { + // The best we can do for return type metadata is to expose + // method-level annotations when the target is the return type... + return this.methodParameter.getMethodAnnotations(); + } + else { + return this.methodParameter.getParameterAnnotations(); + } } else { return new Annotation[0]; diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java similarity index 76% rename from org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java rename to org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java index 17adfa23502..7f65be655c1 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/BeanTypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/PropertyTypeDescriptor.java @@ -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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.beans; +package org.springframework.core.convert.support; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; @@ -35,7 +35,7 @@ import org.springframework.util.ReflectionUtils; * @author Juergen Hoeller * @since 3.0 */ -class BeanTypeDescriptor extends TypeDescriptor { +public class PropertyTypeDescriptor extends TypeDescriptor { private final PropertyDescriptor propertyDescriptor; @@ -45,9 +45,10 @@ class BeanTypeDescriptor extends TypeDescriptor { /** * Create a new BeanTypeDescriptor for the given bean property. * @param propertyDescriptor the corresponding JavaBean PropertyDescriptor + * @param methodParameter the target method parameter */ - public BeanTypeDescriptor(PropertyDescriptor propertyDescriptor) { - super(BeanUtils.getWriteMethodParameter(propertyDescriptor)); + public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter) { + super(methodParameter); this.propertyDescriptor = propertyDescriptor; } @@ -57,7 +58,7 @@ class BeanTypeDescriptor extends TypeDescriptor { * @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) { + public PropertyTypeDescriptor(PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Class type) { super(methodParameter, type); this.propertyDescriptor = propertyDescriptor; } @@ -70,7 +71,6 @@ class BeanTypeDescriptor extends TypeDescriptor { return this.propertyDescriptor; } - @Override public Annotation[] getAnnotations() { Annotation[] anns = this.cachedAnnotations; if (anns == null) { @@ -82,20 +82,22 @@ class BeanTypeDescriptor extends TypeDescriptor { annMap.put(ann.annotationType(), ann); } } - Method targetMethod = getMethodParameter().getMethod(); Method writeMethod = this.propertyDescriptor.getWriteMethod(); Method readMethod = this.propertyDescriptor.getReadMethod(); - if (writeMethod != null && writeMethod != targetMethod) { + if (writeMethod != null && writeMethod != getMethodParameter().getMethod()) { for (Annotation ann : writeMethod.getAnnotations()) { annMap.put(ann.annotationType(), ann); } } - if (readMethod != null && readMethod != targetMethod) { + if (readMethod != null && readMethod != getMethodParameter().getMethod()) { for (Annotation ann : readMethod.getAnnotations()) { annMap.put(ann.annotationType(), ann); } } - for (Annotation ann : targetMethod.getAnnotations()) { + for (Annotation ann : getMethodParameter().getMethodAnnotations()) { + annMap.put(ann.annotationType(), ann); + } + for (Annotation ann : getMethodParameter().getParameterAnnotations()) { annMap.put(ann.annotationType(), ann); } anns = annMap.values().toArray(new Annotation[annMap.size()]); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/BeanTypeDescriptor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/BeanTypeDescriptor.java deleted file mode 100644 index 069f2c08509..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/BeanTypeDescriptor.java +++ /dev/null @@ -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 annMap = new LinkedHashMap(); - 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; - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index d3b8a37960f..07fe6175a36 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -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;