Browse Source

BeanWrapperImpl.getPropertyDescriptor allows for nested paths again

Issue: SPR-13403
pull/867/head
Juergen Hoeller 9 years ago
parent
commit
ff9fb9aa88
  1. 16
      spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java
  2. 40
      spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
  3. 17
      spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java
  4. 1
      spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java
  5. 22
      spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
  6. 7
      spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java

16
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. * Registers default editors.
* @see #setWrappedInstance * @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 * @param registerDefaultEditors whether to register default editors
* (can be suppressed if the accessor won't need any type conversion) * (can be suppressed if the accessor won't need any type conversion)
* @see #setWrappedInstance * @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 * @param object object wrapped by this accessor
*/ */
protected AbstractNestablePropertyAccessor(Object object) { 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 * @param clazz class to instantiate and wrap
*/ */
protected AbstractNestablePropertyAccessor(Class<?> clazz) { 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. * registering a nested path that the object is in.
* @param object object wrapped by this accessor * @param object object wrapped by this accessor
* @param nestedPath the nested path of the object * @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. * registering a nested path that the object is in.
* @param object object wrapped by this accessor * @param object object wrapped by this accessor
* @param nestedPath the nested path of the object * @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 * @param rootObject the root object at the top of the path
*/ */
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { 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)) { if (object.getClass().equals(javaUtilOptionalClass)) {
this.object = OptionalUnwrapper.unwrap(object); this.object = OptionalUnwrapper.unwrap(object);
} }
@ -791,7 +791,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
* @param nestedPath property path we know is nested * @param nestedPath property path we know is nested
* @return last component of the path (the property on the target bean) * @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) { if (pa == this) {
return nestedPath; return nestedPath;
} }

40
spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java

@ -73,8 +73,9 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
*/ */
private AccessControlContext acc; 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. * Registers default editors.
* @see #setWrappedInstance * @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 * @param registerDefaultEditors whether to register default editors
* (can be suppressed if the BeanWrapper won't need any type conversion) * (can be suppressed if the BeanWrapper won't need any type conversion)
* @see #setWrappedInstance * @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 * @param object object wrapped by this BeanWrapper
*/ */
public BeanWrapperImpl(Object object) { 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 * @param clazz class to instantiate and wrap
*/ */
public BeanWrapperImpl(Class<?> clazz) { 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. * registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper * @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object * @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. * registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper * @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object * @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) { private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent) {
super(object, nestedPath, superBw); super(object, nestedPath, parent);
setSecurityContext(superBw.acc); setSecurityContext(parent.acc);
} }
@Override @Override
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
super.setWrappedInstance(object, nestedPath, rootObject); super.setWrappedInstance(object, nestedPath, rootObject);
@ -177,6 +179,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
return this.cachedIntrospectionResults; return this.cachedIntrospectionResults;
} }
/** /**
* Convert the given value for the specified property to the latter's type. * Convert the given value for the specified property to the latter's type.
* <p>This method is only intended for optimizations in a BeanFactory. * <p>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) { private Property property(PropertyDescriptor pd) {
GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd; GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod(), typeAware.getName()); return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName());
} }
@Override @Override
protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) { protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName); PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
@ -236,12 +238,14 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
@Override @Override
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException { public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
BeanPropertyHandler propertyHandler = getLocalPropertyHandler(propertyName); BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
if (propertyHandler == null) { String finalPath = getFinalPath(nestedBw, propertyName);
PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName, throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found"); "No property '" + propertyName + "' found");
} }
return propertyHandler.pd; return pd;
} }
@ -250,8 +254,7 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
private final PropertyDescriptor pd; private final PropertyDescriptor pd;
public BeanPropertyHandler(PropertyDescriptor pd) { public BeanPropertyHandler(PropertyDescriptor pd) {
super(pd.getPropertyType(), super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null);
pd.getReadMethod() != null, pd.getWriteMethod() != null);
this.pd = pd; this.pd = pd;
} }
@ -287,7 +290,6 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
readMethod.setAccessible(true); readMethod.setAccessible(true);
} }
} }
if (System.getSecurityManager() != null) { if (System.getSecurityManager() != null) {
try { try {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {

17
spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java

@ -48,14 +48,27 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<String, FieldPropertyHandler>(); private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<String, FieldPropertyHandler>();
/**
* Create a new DirectFieldAccessor for the given object.
* @param object object wrapped by this DirectFieldAccessor
*/
public DirectFieldAccessor(Object object) { public DirectFieldAccessor(Object object) {
super(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 @Override
protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) { protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) {
FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName); FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName);

1
spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java

@ -214,7 +214,6 @@ public abstract class AbstractPropertyAccessorTests {
public void getNestedDeepProperty() { public void getNestedDeepProperty() {
Person target = createPerson("John", "London", "UK"); Person target = createPerson("John", "London", "UK");
AbstractPropertyAccessor accessor = createAccessor(target); AbstractPropertyAccessor accessor = createAccessor(target);
assertThat(accessor.getPropertyValue("address.country.name"), is("UK")); assertThat(accessor.getPropertyValue("address.country.name"), is("UK"));
} }

22
spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java

@ -36,15 +36,16 @@ import static org.junit.Assert.*;
* @author Chris Beams * @author Chris Beams
* @author Dave Syer * @author Dave Syer
*/ */
public final class BeanWrapperTests extends AbstractPropertyAccessorTests { public class BeanWrapperTests extends AbstractPropertyAccessorTests {
@Override @Override
protected BeanWrapperImpl createAccessor(Object target) { protected BeanWrapperImpl createAccessor(Object target) {
return new BeanWrapperImpl(target); return new BeanWrapperImpl(target);
} }
@Test @Test
public void setterDoestNotCallGetter() { public void setterDoesNotCallGetter() {
GetterBean target = new GetterBean(); GetterBean target = new GetterBean();
BeanWrapper accessor = createAccessor(target); BeanWrapper accessor = createAccessor(target);
accessor.setPropertyValue("name", "tom"); 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() { public void setPropertyTypeMismatch() {
PropertyTypeMismatch target = new PropertyTypeMismatch(); PropertyTypeMismatch target = new PropertyTypeMismatch();
BeanWrapper accessor = createAccessor(target); BeanWrapper accessor = createAccessor(target);
@ -143,6 +144,21 @@ public final class BeanWrapperTests extends AbstractPropertyAccessorTests {
assertEquals(8, accessor.getPropertyValue("object")); 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 @Test
public void getPropertyWithOptional() { public void getPropertyWithOptional() {
GetterWithOptional target = new GetterWithOptional(); GetterWithOptional target = new GetterWithOptional();

7
spring-beans/src/test/java/org/springframework/beans/DirectFieldAccessorTests.java

@ -27,7 +27,7 @@ import static org.junit.Assert.*;
* *
* @author Jose Luis Martin * @author Jose Luis Martin
* @author Chris Beams * @author Chris Beams
* @@author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests { public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests {
@ -39,14 +39,17 @@ public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests {
@Test @Test
public void withShadowedField() throws Exception { public void withShadowedField() throws Exception {
final StringBuilder sb = new StringBuilder();
@SuppressWarnings("serial") @SuppressWarnings("serial")
TestBean target = new TestBean() { TestBean target = new TestBean() {
@SuppressWarnings("unused") @SuppressWarnings("unused")
StringBuilder name = new StringBuilder(); StringBuilder name = sb;
}; };
DirectFieldAccessor dfa = createAccessor(target); DirectFieldAccessor dfa = createAccessor(target);
assertEquals(StringBuilder.class, dfa.getPropertyType("name")); assertEquals(StringBuilder.class, dfa.getPropertyType("name"));
assertEquals(sb, dfa.getPropertyValue("name"));
} }
} }

Loading…
Cancel
Save