From ff9fb9aa88eadf84d56851b2b687b2e34dd21dc5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Aug 2015 11:17:42 +0200 Subject: [PATCH] BeanWrapperImpl.getPropertyDescriptor allows for nested paths again Issue: SPR-13403 --- .../AbstractNestablePropertyAccessor.java | 16 ++++---- .../beans/BeanWrapperImpl.java | 40 ++++++++++--------- .../beans/DirectFieldAccessor.java | 17 +++++++- .../beans/AbstractPropertyAccessorTests.java | 1 - .../beans/BeanWrapperTests.java | 22 ++++++++-- .../beans/DirectFieldAccessorTests.java | 7 +++- 6 files changed, 68 insertions(+), 35 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index 9ea5bfd372..dca277179f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -103,7 +103,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA /** - * Create new empty accessor. Wrapped instance needs to be set afterwards. + * Create a new empty accessor. Wrapped instance needs to be set afterwards. * Registers default editors. * @see #setWrappedInstance */ @@ -112,7 +112,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } /** - * Create new empty accessor. Wrapped instance needs to be set afterwards. + * Create a new empty accessor. Wrapped instance needs to be set afterwards. * @param registerDefaultEditors whether to register default editors * (can be suppressed if the accessor won't need any type conversion) * @see #setWrappedInstance @@ -125,7 +125,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } /** - * Create new accessor for the given object. + * Create a new accessor for the given object. * @param object object wrapped by this accessor */ protected AbstractNestablePropertyAccessor(Object object) { @@ -134,7 +134,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } /** - * Create new accessor, wrapping a new instance of the specified class. + * Create a new accessor, wrapping a new instance of the specified class. * @param clazz class to instantiate and wrap */ protected AbstractNestablePropertyAccessor(Class clazz) { @@ -143,7 +143,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } /** - * Create new accessor for the given object, + * Create a new accessor for the given object, * registering a nested path that the object is in. * @param object object wrapped by this accessor * @param nestedPath the nested path of the object @@ -155,7 +155,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } /** - * Create new accessor for the given object, + * Create a new accessor for the given object, * registering a nested path that the object is in. * @param object object wrapped by this accessor * @param nestedPath the nested path of the object @@ -202,7 +202,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA * @param rootObject the root object at the top of the path */ public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { - Assert.notNull(object, "Bean object must not be null"); + Assert.notNull(object, "Target object must not be null"); if (object.getClass().equals(javaUtilOptionalClass)) { this.object = OptionalUnwrapper.unwrap(object); } @@ -791,7 +791,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA * @param nestedPath property path we know is nested * @return last component of the path (the property on the target bean) */ - private String getFinalPath(AbstractNestablePropertyAccessor pa, String nestedPath) { + protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nestedPath) { if (pa == this) { return nestedPath; } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 637da375bc..6625cc1aa4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -73,8 +73,9 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements */ private AccessControlContext acc; + /** - * Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. + * Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. * Registers default editors. * @see #setWrappedInstance */ @@ -83,7 +84,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } /** - * Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. + * Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards. * @param registerDefaultEditors whether to register default editors * (can be suppressed if the BeanWrapper won't need any type conversion) * @see #setWrappedInstance @@ -93,7 +94,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } /** - * Create new BeanWrapperImpl for the given object. + * Create a new BeanWrapperImpl for the given object. * @param object object wrapped by this BeanWrapper */ public BeanWrapperImpl(Object object) { @@ -101,7 +102,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } /** - * Create new BeanWrapperImpl, wrapping a new instance of the specified class. + * Create a new BeanWrapperImpl, wrapping a new instance of the specified class. * @param clazz class to instantiate and wrap */ public BeanWrapperImpl(Class clazz) { @@ -109,7 +110,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } /** - * Create new BeanWrapperImpl for the given object, + * Create a new BeanWrapperImpl for the given object, * registering a nested path that the object is in. * @param object object wrapped by this BeanWrapper * @param nestedPath the nested path of the object @@ -120,17 +121,18 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } /** - * Create new BeanWrapperImpl for the given object, + * Create a new BeanWrapperImpl for the given object, * registering a nested path that the object is in. * @param object object wrapped by this BeanWrapper * @param nestedPath the nested path of the object - * @param superBw the containing BeanWrapper (must not be {@code null}) + * @param parent the containing BeanWrapper (must not be {@code null}) */ - private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl superBw) { - super(object, nestedPath, superBw); - setSecurityContext(superBw.acc); + private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent) { + super(object, nestedPath, parent); + setSecurityContext(parent.acc); } + @Override public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { super.setWrappedInstance(object, nestedPath, rootObject); @@ -177,6 +179,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements return this.cachedIntrospectionResults; } + /** * Convert the given value for the specified property to the latter's type. *

This method is only intended for optimizations in a BeanFactory. @@ -202,11 +205,10 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } private Property property(PropertyDescriptor pd) { - GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd; - return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod(), typeAware.getName()); + GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd; + return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName()); } - @Override protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) { PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); @@ -236,12 +238,14 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements @Override public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException { - BeanPropertyHandler propertyHandler = getLocalPropertyHandler(propertyName); - if (propertyHandler == null) { + BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName); + String finalPath = getFinalPath(nestedBw, propertyName); + PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath); + if (pd == null) { throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, "No property '" + propertyName + "' found"); } - return propertyHandler.pd; + return pd; } @@ -250,8 +254,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements private final PropertyDescriptor pd; public BeanPropertyHandler(PropertyDescriptor pd) { - super(pd.getPropertyType(), - pd.getReadMethod() != null, pd.getWriteMethod() != null); + super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null); this.pd = pd; } @@ -287,7 +290,6 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements readMethod.setAccessible(true); } } - if (System.getSecurityManager() != null) { try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { diff --git a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java index 687d55d46d..356231132d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java @@ -48,14 +48,27 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor { private final Map fieldMap = new HashMap(); + + /** + * Create a new DirectFieldAccessor for the given object. + * @param object object wrapped by this DirectFieldAccessor + */ public DirectFieldAccessor(Object object) { super(object); } - protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor superBw) { - super(object, nestedPath, superBw); + /** + * Create a new DirectFieldAccessor for the given object, + * registering a nested path that the object is in. + * @param object object wrapped by this DirectFieldAccessor + * @param nestedPath the nested path of the object + * @param parent the containing DirectFieldAccessor (must not be {@code null}) + */ + protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor parent) { + super(object, nestedPath, parent); } + @Override protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) { FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName); diff --git a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java index 0180fbc42d..14118896c0 100644 --- a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java @@ -214,7 +214,6 @@ public abstract class AbstractPropertyAccessorTests { public void getNestedDeepProperty() { Person target = createPerson("John", "London", "UK"); AbstractPropertyAccessor accessor = createAccessor(target); - assertThat(accessor.getPropertyValue("address.country.name"), is("UK")); } diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index a46275f972..7a3232b1b1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -36,15 +36,16 @@ import static org.junit.Assert.*; * @author Chris Beams * @author Dave Syer */ -public final class BeanWrapperTests extends AbstractPropertyAccessorTests { +public class BeanWrapperTests extends AbstractPropertyAccessorTests { @Override protected BeanWrapperImpl createAccessor(Object target) { return new BeanWrapperImpl(target); } + @Test - public void setterDoestNotCallGetter() { + public void setterDoesNotCallGetter() { GetterBean target = new GetterBean(); BeanWrapper accessor = createAccessor(target); accessor.setPropertyValue("name", "tom"); @@ -133,7 +134,7 @@ public final class BeanWrapperTests extends AbstractPropertyAccessorTests { } } - @Test // Can't be shared: no type mismatch with a field") + @Test // Can't be shared: no type mismatch with a field public void setPropertyTypeMismatch() { PropertyTypeMismatch target = new PropertyTypeMismatch(); BeanWrapper accessor = createAccessor(target); @@ -143,6 +144,21 @@ public final class BeanWrapperTests extends AbstractPropertyAccessorTests { assertEquals(8, accessor.getPropertyValue("object")); } + @Test + public void propertyDescriptors() { + TestBean target = new TestBean(); + target.setSpouse(new TestBean()); + BeanWrapper accessor = createAccessor(target); + accessor.setPropertyValue("name", "a"); + accessor.setPropertyValue("spouse.name", "b"); + assertEquals("a", target.getName()); + assertEquals("b", target.getSpouse().getName()); + assertEquals("a", accessor.getPropertyValue("name")); + assertEquals("b", accessor.getPropertyValue("spouse.name")); + assertEquals(String.class, accessor.getPropertyDescriptor("name").getPropertyType()); + assertEquals(String.class, accessor.getPropertyDescriptor("spouse.name").getPropertyType()); + } + @Test public void getPropertyWithOptional() { GetterWithOptional target = new GetterWithOptional(); diff --git a/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java index 4145e7bb9b..e67bae0c26 100644 --- a/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java @@ -27,7 +27,7 @@ import static org.junit.Assert.*; * * @author Jose Luis Martin * @author Chris Beams - * @@author Stephane Nicoll + * @author Stephane Nicoll */ public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests { @@ -39,14 +39,17 @@ public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests { @Test public void withShadowedField() throws Exception { + final StringBuilder sb = new StringBuilder(); + @SuppressWarnings("serial") TestBean target = new TestBean() { @SuppressWarnings("unused") - StringBuilder name = new StringBuilder(); + StringBuilder name = sb; }; DirectFieldAccessor dfa = createAccessor(target); assertEquals(StringBuilder.class, dfa.getPropertyType("name")); + assertEquals(sb, dfa.getPropertyValue("name")); } }