Skip to content

Commit ff9fb9a

Browse files
committed
BeanWrapperImpl.getPropertyDescriptor allows for nested paths again
Issue: SPR-13403
1 parent 90f46f9 commit ff9fb9a

File tree

6 files changed

+68
-35
lines changed

6 files changed

+68
-35
lines changed

spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
103103

104104

105105
/**
106-
* Create new empty accessor. Wrapped instance needs to be set afterwards.
106+
* Create a new empty accessor. Wrapped instance needs to be set afterwards.
107107
* Registers default editors.
108108
* @see #setWrappedInstance
109109
*/
@@ -112,7 +112,7 @@ protected AbstractNestablePropertyAccessor() {
112112
}
113113

114114
/**
115-
* Create new empty accessor. Wrapped instance needs to be set afterwards.
115+
* Create a new empty accessor. Wrapped instance needs to be set afterwards.
116116
* @param registerDefaultEditors whether to register default editors
117117
* (can be suppressed if the accessor won't need any type conversion)
118118
* @see #setWrappedInstance
@@ -125,7 +125,7 @@ protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) {
125125
}
126126

127127
/**
128-
* Create new accessor for the given object.
128+
* Create a new accessor for the given object.
129129
* @param object object wrapped by this accessor
130130
*/
131131
protected AbstractNestablePropertyAccessor(Object object) {
@@ -134,7 +134,7 @@ protected AbstractNestablePropertyAccessor(Object object) {
134134
}
135135

136136
/**
137-
* Create new accessor, wrapping a new instance of the specified class.
137+
* Create a new accessor, wrapping a new instance of the specified class.
138138
* @param clazz class to instantiate and wrap
139139
*/
140140
protected AbstractNestablePropertyAccessor(Class<?> clazz) {
@@ -143,7 +143,7 @@ protected AbstractNestablePropertyAccessor(Class<?> clazz) {
143143
}
144144

145145
/**
146-
* Create new accessor for the given object,
146+
* Create a new accessor for the given object,
147147
* registering a nested path that the object is in.
148148
* @param object object wrapped by this accessor
149149
* @param nestedPath the nested path of the object
@@ -155,7 +155,7 @@ protected AbstractNestablePropertyAccessor(Object object, String nestedPath, Obj
155155
}
156156

157157
/**
158-
* Create new accessor for the given object,
158+
* Create a new accessor for the given object,
159159
* registering a nested path that the object is in.
160160
* @param object object wrapped by this accessor
161161
* @param nestedPath the nested path of the object
@@ -202,7 +202,7 @@ public void setWrappedInstance(Object object) {
202202
* @param rootObject the root object at the top of the path
203203
*/
204204
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
205-
Assert.notNull(object, "Bean object must not be null");
205+
Assert.notNull(object, "Target object must not be null");
206206
if (object.getClass().equals(javaUtilOptionalClass)) {
207207
this.object = OptionalUnwrapper.unwrap(object);
208208
}
@@ -791,7 +791,7 @@ private void growCollectionIfNecessary(Collection<Object> collection, int index,
791791
* @param nestedPath property path we know is nested
792792
* @return last component of the path (the property on the target bean)
793793
*/
794-
private String getFinalPath(AbstractNestablePropertyAccessor pa, String nestedPath) {
794+
protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nestedPath) {
795795
if (pa == this) {
796796
return nestedPath;
797797
}

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

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
7373
*/
7474
private AccessControlContext acc;
7575

76+
7677
/**
77-
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
78+
* Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
7879
* Registers default editors.
7980
* @see #setWrappedInstance
8081
*/
@@ -83,7 +84,7 @@ public BeanWrapperImpl() {
8384
}
8485

8586
/**
86-
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
87+
* Create a new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
8788
* @param registerDefaultEditors whether to register default editors
8889
* (can be suppressed if the BeanWrapper won't need any type conversion)
8990
* @see #setWrappedInstance
@@ -93,23 +94,23 @@ public BeanWrapperImpl(boolean registerDefaultEditors) {
9394
}
9495

9596
/**
96-
* Create new BeanWrapperImpl for the given object.
97+
* Create a new BeanWrapperImpl for the given object.
9798
* @param object object wrapped by this BeanWrapper
9899
*/
99100
public BeanWrapperImpl(Object object) {
100101
super(object);
101102
}
102103

103104
/**
104-
* Create new BeanWrapperImpl, wrapping a new instance of the specified class.
105+
* Create a new BeanWrapperImpl, wrapping a new instance of the specified class.
105106
* @param clazz class to instantiate and wrap
106107
*/
107108
public BeanWrapperImpl(Class<?> clazz) {
108109
super(clazz);
109110
}
110111

111112
/**
112-
* Create new BeanWrapperImpl for the given object,
113+
* Create a new BeanWrapperImpl for the given object,
113114
* registering a nested path that the object is in.
114115
* @param object object wrapped by this BeanWrapper
115116
* @param nestedPath the nested path of the object
@@ -120,17 +121,18 @@ public BeanWrapperImpl(Object object, String nestedPath, Object rootObject) {
120121
}
121122

122123
/**
123-
* Create new BeanWrapperImpl for the given object,
124+
* Create a new BeanWrapperImpl for the given object,
124125
* registering a nested path that the object is in.
125126
* @param object object wrapped by this BeanWrapper
126127
* @param nestedPath the nested path of the object
127-
* @param superBw the containing BeanWrapper (must not be {@code null})
128+
* @param parent the containing BeanWrapper (must not be {@code null})
128129
*/
129-
private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl superBw) {
130-
super(object, nestedPath, superBw);
131-
setSecurityContext(superBw.acc);
130+
private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent) {
131+
super(object, nestedPath, parent);
132+
setSecurityContext(parent.acc);
132133
}
133134

135+
134136
@Override
135137
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
136138
super.setWrappedInstance(object, nestedPath, rootObject);
@@ -177,6 +179,7 @@ private CachedIntrospectionResults getCachedIntrospectionResults() {
177179
return this.cachedIntrospectionResults;
178180
}
179181

182+
180183
/**
181184
* Convert the given value for the specified property to the latter's type.
182185
* <p>This method is only intended for optimizations in a BeanFactory.
@@ -202,11 +205,10 @@ public Object convertForProperty(Object value, String propertyName) throws TypeM
202205
}
203206

204207
private Property property(PropertyDescriptor pd) {
205-
GenericTypeAwarePropertyDescriptor typeAware = (GenericTypeAwarePropertyDescriptor) pd;
206-
return new Property(typeAware.getBeanClass(), typeAware.getReadMethod(), typeAware.getWriteMethod(), typeAware.getName());
208+
GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
209+
return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName());
207210
}
208211

209-
210212
@Override
211213
protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
212214
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
@@ -236,12 +238,14 @@ public PropertyDescriptor[] getPropertyDescriptors() {
236238

237239
@Override
238240
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
239-
BeanPropertyHandler propertyHandler = getLocalPropertyHandler(propertyName);
240-
if (propertyHandler == null) {
241+
BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
242+
String finalPath = getFinalPath(nestedBw, propertyName);
243+
PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
244+
if (pd == null) {
241245
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
242246
"No property '" + propertyName + "' found");
243247
}
244-
return propertyHandler.pd;
248+
return pd;
245249
}
246250

247251

@@ -250,8 +254,7 @@ private class BeanPropertyHandler extends PropertyHandler {
250254
private final PropertyDescriptor pd;
251255

252256
public BeanPropertyHandler(PropertyDescriptor pd) {
253-
super(pd.getPropertyType(),
254-
pd.getReadMethod() != null, pd.getWriteMethod() != null);
257+
super(pd.getPropertyType(), pd.getReadMethod() != null, pd.getWriteMethod() != null);
255258
this.pd = pd;
256259
}
257260

@@ -287,7 +290,6 @@ public Object run() {
287290
readMethod.setAccessible(true);
288291
}
289292
}
290-
291293
if (System.getSecurityManager() != null) {
292294
try {
293295
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,27 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
4848

4949
private final Map<String, FieldPropertyHandler> fieldMap = new HashMap<String, FieldPropertyHandler>();
5050

51+
52+
/**
53+
* Create a new DirectFieldAccessor for the given object.
54+
* @param object object wrapped by this DirectFieldAccessor
55+
*/
5156
public DirectFieldAccessor(Object object) {
5257
super(object);
5358
}
5459

55-
protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor superBw) {
56-
super(object, nestedPath, superBw);
60+
/**
61+
* Create a new DirectFieldAccessor for the given object,
62+
* registering a nested path that the object is in.
63+
* @param object object wrapped by this DirectFieldAccessor
64+
* @param nestedPath the nested path of the object
65+
* @param parent the containing DirectFieldAccessor (must not be {@code null})
66+
*/
67+
protected DirectFieldAccessor(Object object, String nestedPath, DirectFieldAccessor parent) {
68+
super(object, nestedPath, parent);
5769
}
5870

71+
5972
@Override
6073
protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) {
6174
FieldPropertyHandler propertyHandler = this.fieldMap.get(propertyName);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ public void getNestedProperty() {
214214
public void getNestedDeepProperty() {
215215
Person target = createPerson("John", "London", "UK");
216216
AbstractPropertyAccessor accessor = createAccessor(target);
217-
218217
assertThat(accessor.getPropertyValue("address.country.name"), is("UK"));
219218
}
220219

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@
3636
* @author Chris Beams
3737
* @author Dave Syer
3838
*/
39-
public final class BeanWrapperTests extends AbstractPropertyAccessorTests {
39+
public class BeanWrapperTests extends AbstractPropertyAccessorTests {
4040

4141
@Override
4242
protected BeanWrapperImpl createAccessor(Object target) {
4343
return new BeanWrapperImpl(target);
4444
}
4545

46+
4647
@Test
47-
public void setterDoestNotCallGetter() {
48+
public void setterDoesNotCallGetter() {
4849
GetterBean target = new GetterBean();
4950
BeanWrapper accessor = createAccessor(target);
5051
accessor.setPropertyValue("name", "tom");
@@ -133,7 +134,7 @@ public void notWritablePropertyExceptionContainsAlternativeMatches() {
133134
}
134135
}
135136

136-
@Test // Can't be shared: no type mismatch with a field")
137+
@Test // Can't be shared: no type mismatch with a field
137138
public void setPropertyTypeMismatch() {
138139
PropertyTypeMismatch target = new PropertyTypeMismatch();
139140
BeanWrapper accessor = createAccessor(target);
@@ -143,6 +144,21 @@ public void setPropertyTypeMismatch() {
143144
assertEquals(8, accessor.getPropertyValue("object"));
144145
}
145146

147+
@Test
148+
public void propertyDescriptors() {
149+
TestBean target = new TestBean();
150+
target.setSpouse(new TestBean());
151+
BeanWrapper accessor = createAccessor(target);
152+
accessor.setPropertyValue("name", "a");
153+
accessor.setPropertyValue("spouse.name", "b");
154+
assertEquals("a", target.getName());
155+
assertEquals("b", target.getSpouse().getName());
156+
assertEquals("a", accessor.getPropertyValue("name"));
157+
assertEquals("b", accessor.getPropertyValue("spouse.name"));
158+
assertEquals(String.class, accessor.getPropertyDescriptor("name").getPropertyType());
159+
assertEquals(String.class, accessor.getPropertyDescriptor("spouse.name").getPropertyType());
160+
}
161+
146162
@Test
147163
public void getPropertyWithOptional() {
148164
GetterWithOptional target = new GetterWithOptional();

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
*
2828
* @author Jose Luis Martin
2929
* @author Chris Beams
30-
* @@author Stephane Nicoll
30+
* @author Stephane Nicoll
3131
*/
3232
public class DirectFieldAccessorTests extends AbstractPropertyAccessorTests {
3333

@@ -39,14 +39,17 @@ protected DirectFieldAccessor createAccessor(Object target) {
3939

4040
@Test
4141
public void withShadowedField() throws Exception {
42+
final StringBuilder sb = new StringBuilder();
43+
4244
@SuppressWarnings("serial")
4345
TestBean target = new TestBean() {
4446
@SuppressWarnings("unused")
45-
StringBuilder name = new StringBuilder();
47+
StringBuilder name = sb;
4648
};
4749

4850
DirectFieldAccessor dfa = createAccessor(target);
4951
assertEquals(StringBuilder.class, dfa.getPropertyType("name"));
52+
assertEquals(sb, dfa.getPropertyValue("name"));
5053
}
5154

5255
}

0 commit comments

Comments
 (0)