Skip to content

Commit 4b0bf16

Browse files
committed
Select ambiguous write method based on read method (matching its return type)
Also avoids unnecessary checks in name-based PropertyDescriptor constructor. See gh-29320
1 parent 33023b2 commit 4b0bf16

File tree

3 files changed

+72
-25
lines changed

3 files changed

+72
-25
lines changed

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

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
import java.beans.Introspector;
2121
import java.beans.PropertyDescriptor;
2222
import java.lang.reflect.Method;
23+
import java.util.ArrayList;
2324
import java.util.Collection;
2425
import java.util.Enumeration;
26+
import java.util.List;
2527
import java.util.Map;
2628
import java.util.TreeMap;
2729

@@ -52,8 +54,10 @@ abstract class PropertyDescriptorUtils {
5254
* @see SimpleBeanInfoFactory
5355
* @see java.beans.Introspector#getBeanInfo(Class)
5456
*/
55-
public static Collection<PropertyDescriptor> determineBasicProperties(Class<?> beanClass) throws IntrospectionException {
56-
Map<String, PropertyDescriptor> pdMap = new TreeMap<>();
57+
public static Collection<? extends PropertyDescriptor> determineBasicProperties(Class<?> beanClass)
58+
throws IntrospectionException {
59+
60+
Map<String, BasicPropertyDescriptor> pdMap = new TreeMap<>();
5761

5862
for (Method method : beanClass.getMethods()) {
5963
String methodName = method.getName();
@@ -81,39 +85,26 @@ else if (methodName.startsWith("is") && method.getParameterCount() == 0 && metho
8185
continue;
8286
}
8387

84-
PropertyDescriptor pd = pdMap.get(propertyName);
88+
BasicPropertyDescriptor pd = pdMap.get(propertyName);
8589
if (pd != null) {
8690
if (setter) {
8791
if (pd.getWriteMethod() == null ||
8892
pd.getWriteMethod().getParameterTypes()[0].isAssignableFrom(method.getParameterTypes()[0])) {
89-
try {
90-
pd.setWriteMethod(method);
91-
}
92-
catch (IntrospectionException ex) {
93-
// typically a type mismatch -> ignore
94-
}
93+
pd.setWriteMethod(method);
94+
}
95+
else {
96+
pd.addWriteMethod(method);
9597
}
9698
}
9799
else {
98100
if (pd.getReadMethod() == null ||
99101
(pd.getReadMethod().getReturnType() == method.getReturnType() && method.getName().startsWith("is"))) {
100-
try {
101-
pd.setReadMethod(method);
102-
}
103-
catch (IntrospectionException ex) {
104-
// typically a type mismatch -> ignore
105-
}
102+
pd.setReadMethod(method);
106103
}
107104
}
108105
}
109106
else {
110-
pd = new BasicPropertyDescriptor(propertyName, beanClass);
111-
if (setter) {
112-
pd.setWriteMethod(method);
113-
}
114-
else {
115-
pd.setReadMethod(method);
116-
}
107+
pd = new BasicPropertyDescriptor(propertyName, (!setter ? method : null), (setter ? method : null));
117108
pdMap.put(propertyName, pd);
118109
}
119110
}
@@ -277,8 +268,12 @@ private static class BasicPropertyDescriptor extends PropertyDescriptor {
277268
@Nullable
278269
private Method writeMethod;
279270

280-
public BasicPropertyDescriptor(String propertyName, Class<?> beanClass) throws IntrospectionException {
281-
super(propertyName, beanClass, null, null);
271+
private final List<Method> alternativeWriteMethods = new ArrayList<>();
272+
273+
public BasicPropertyDescriptor(String propertyName, @Nullable Method readMethod, @Nullable Method writeMethod)
274+
throws IntrospectionException {
275+
276+
super(propertyName, readMethod, writeMethod);
282277
}
283278

284279
@Override
@@ -297,11 +292,33 @@ public void setWriteMethod(@Nullable Method writeMethod) {
297292
this.writeMethod = writeMethod;
298293
}
299294

295+
public void addWriteMethod(Method writeMethod) {
296+
if (this.writeMethod != null) {
297+
this.alternativeWriteMethods.add(this.writeMethod);
298+
this.writeMethod = null;
299+
}
300+
this.alternativeWriteMethods.add(writeMethod);
301+
}
302+
300303
@Override
301304
@Nullable
302305
public Method getWriteMethod() {
306+
if (this.writeMethod == null && !this.alternativeWriteMethods.isEmpty()) {
307+
if (this.readMethod == null) {
308+
return this.alternativeWriteMethods.get(0);
309+
}
310+
else {
311+
for (Method method : this.alternativeWriteMethods) {
312+
if (this.readMethod.getReturnType().isAssignableFrom(method.getParameterTypes()[0])) {
313+
this.writeMethod = method;
314+
break;
315+
}
316+
}
317+
}
318+
}
303319
return this.writeMethod;
304320
}
305321
}
306322

323+
307324
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ public class SimpleBeanInfoFactory implements BeanInfoFactory, Ordered {
4747
@Override
4848
@NonNull
4949
public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
50-
Collection<PropertyDescriptor> pds = PropertyDescriptorUtils.determineBasicProperties(beanClass);
50+
Collection<? extends PropertyDescriptor> pds =
51+
PropertyDescriptorUtils.determineBasicProperties(beanClass);
52+
5153
return new SimpleBeanInfo() {
5254
@Override
5355
public PropertyDescriptor[] getPropertyDescriptors() {

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ void setPropertyTypeMismatch() {
153153
assertThat(accessor.getPropertyValue("object")).isEqualTo(8);
154154
}
155155

156+
@Test
157+
void setterOverload() {
158+
SetterOverload target = new SetterOverload();
159+
BeanWrapper accessor = createAccessor(target);
160+
accessor.setPropertyValue("object", "a String");
161+
assertThat(target.value).isEqualTo("a String");
162+
assertThat(target.getObject()).isEqualTo("a String");
163+
assertThat(accessor.getPropertyValue("object")).isEqualTo("a String");
164+
}
165+
156166
@Test
157167
void propertyDescriptors() throws Exception {
158168
TestBean target = new TestBean();
@@ -348,6 +358,24 @@ public Integer getObject() {
348358
}
349359

350360

361+
public static class SetterOverload {
362+
363+
public String value;
364+
365+
public void setObject(Integer length) {
366+
this.value = length.toString();
367+
}
368+
369+
public void setObject(String object) {
370+
this.value = object;
371+
}
372+
373+
public String getObject() {
374+
return this.value;
375+
}
376+
}
377+
378+
351379
public static class GetterWithOptional {
352380

353381
public TestBean value;

0 commit comments

Comments
 (0)