Skip to content

Commit c49b9be

Browse files
committed
Merge branch '2.1.x'
Closes gh-16859
2 parents a82b526 + ab15b8e commit c49b9be

File tree

2 files changed

+109
-21
lines changed

2 files changed

+109
-21
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2018 the original author or authors.
2+
* Copyright 2012-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -97,8 +97,10 @@ else if (value == null || !bound.equals(value.get())) {
9797

9898
/**
9999
* The bean being bound.
100+
*
101+
* @param <T> the bean type
100102
*/
101-
private static class Bean<T> {
103+
static class Bean<T> {
102104

103105
private static Bean<?> cached;
104106

@@ -111,23 +113,36 @@ private static class Bean<T> {
111113
Bean(ResolvableType type, Class<?> resolvedType) {
112114
this.type = type;
113115
this.resolvedType = resolvedType;
114-
putProperties(resolvedType);
116+
addProperties(resolvedType);
115117
}
116118

117-
private void putProperties(Class<?> type) {
119+
private void addProperties(Class<?> type) {
118120
while (type != null && !Object.class.equals(type)) {
119-
for (Method method : type.getDeclaredMethods()) {
120-
if (isCandidate(method)) {
121-
addMethod(method);
122-
}
123-
}
124-
for (Field field : type.getDeclaredFields()) {
125-
addField(field);
126-
}
121+
Method[] declaredMethods = type.getDeclaredMethods();
122+
Field[] declaredFields = type.getDeclaredFields();
123+
addProperties(declaredMethods, declaredFields);
127124
type = type.getSuperclass();
128125
}
129126
}
130127

128+
protected void addProperties(Method[] declaredMethods, Field[] declaredFields) {
129+
for (int i = 0; i < declaredMethods.length; i++) {
130+
if (!isCandidate(declaredMethods[i])) {
131+
declaredMethods[i] = null;
132+
}
133+
}
134+
for (Method method : declaredMethods) {
135+
addMethodIfPossible(method, "get", 0, BeanProperty::addGetter);
136+
}
137+
for (Method method : declaredMethods) {
138+
addMethodIfPossible(method, "is", 0, BeanProperty::addGetter);
139+
addMethodIfPossible(method, "set", 1, BeanProperty::addSetter);
140+
}
141+
for (Field field : declaredFields) {
142+
addField(field);
143+
}
144+
}
145+
131146
private boolean isCandidate(Method method) {
132147
int modifiers = method.getModifiers();
133148
return Modifier.isPublic(modifiers) && !Modifier.isAbstract(modifiers)
@@ -136,15 +151,9 @@ private boolean isCandidate(Method method) {
136151
&& !Class.class.equals(method.getDeclaringClass());
137152
}
138153

139-
private void addMethod(Method method) {
140-
addMethodIfPossible(method, "get", 0, BeanProperty::addGetter);
141-
addMethodIfPossible(method, "is", 0, BeanProperty::addGetter);
142-
addMethodIfPossible(method, "set", 1, BeanProperty::addSetter);
143-
}
144-
145154
private void addMethodIfPossible(Method method, String prefix, int parameterCount,
146155
BiConsumer<BeanProperty, Method> consumer) {
147-
if (method.getParameterCount() == parameterCount
156+
if (method != null && method.getParameterCount() == parameterCount
148157
&& method.getName().startsWith(prefix)
149158
&& method.getName().length() > prefix.length()) {
150159
String propertyName = Introspector
@@ -250,7 +259,7 @@ public T get() {
250259
/**
251260
* A bean property being bound.
252261
*/
253-
private static class BeanProperty {
262+
static class BeanProperty {
254263

255264
private final String name;
256265

@@ -274,11 +283,16 @@ public void addGetter(Method getter) {
274283
}
275284

276285
public void addSetter(Method setter) {
277-
if (this.setter == null) {
286+
if (this.setter == null || isBetterSetter(setter)) {
278287
this.setter = setter;
279288
}
280289
}
281290

291+
private boolean isBetterSetter(Method setter) {
292+
return this.getter != null
293+
&& this.getter.getReturnType().equals(setter.getParameterTypes()[0]);
294+
}
295+
282296
public void addField(Field field) {
283297
if (this.field == null) {
284298
this.field = field;

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/JavaBeanBinderTests.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.boot.context.properties.bind;
1818

19+
import java.lang.reflect.Field;
20+
import java.lang.reflect.Method;
1921
import java.time.LocalDate;
2022
import java.util.ArrayList;
2123
import java.util.Collection;
@@ -28,11 +30,14 @@
2830
import org.junit.Before;
2931
import org.junit.Test;
3032

33+
import org.springframework.boot.context.properties.bind.JavaBeanBinder.Bean;
34+
import org.springframework.boot.context.properties.bind.JavaBeanBinder.BeanProperty;
3135
import org.springframework.boot.context.properties.bind.handler.IgnoreErrorsBindHandler;
3236
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
3337
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
3438
import org.springframework.boot.context.properties.source.MockConfigurationPropertySource;
3539
import org.springframework.boot.convert.Delimiter;
40+
import org.springframework.core.ResolvableType;
3641
import org.springframework.format.annotation.DateTimeFormat;
3742

3843
import static org.assertj.core.api.Assertions.assertThat;
@@ -44,6 +49,7 @@
4449
*
4550
* @author Phillip Webb
4651
* @author Madhura Bhave
52+
* @author Andy Wilkinson
4753
*/
4854
public class JavaBeanBinderTests {
4955

@@ -507,6 +513,56 @@ public void bindToClassShouldCacheWithGenerics() {
507513
assertThat(bean.getBooleans().get("b").getValue()).isEqualTo(true);
508514
}
509515

516+
public void bindToClassWithOverloadedSetterShouldUseSetterThatMatchesField() {
517+
// gh-16206
518+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
519+
source.put("foo.property", "some string");
520+
this.sources.add(source);
521+
PropertyWithOverloadedSetter bean = this.binder
522+
.bind("foo", Bindable.of(PropertyWithOverloadedSetter.class)).get();
523+
assertThat(bean.getProperty()).isEqualTo("some string");
524+
}
525+
526+
@Test
527+
public void beanProperiesPreferMatchingType() {
528+
// gh-16206
529+
530+
ResolvableType type = ResolvableType.forClass(PropertyWithOverloadedSetter.class);
531+
Bean<PropertyWithOverloadedSetter> bean = new Bean<PropertyWithOverloadedSetter>(
532+
type, type.resolve()) {
533+
534+
@Override
535+
protected void addProperties(Method[] declaredMethods,
536+
Field[] declaredFields) {
537+
// We override here because we need a specific order of the declared
538+
// methods and the JVM doesn't give us one
539+
int intSetter = -1;
540+
int stringSetter = -1;
541+
for (int i = 0; i < declaredMethods.length; i++) {
542+
Method method = declaredMethods[i];
543+
if (method.getName().equals("setProperty")) {
544+
if (method.getParameters()[0].getType().equals(int.class)) {
545+
intSetter = i;
546+
}
547+
else {
548+
stringSetter = i;
549+
}
550+
}
551+
}
552+
if (intSetter > stringSetter) {
553+
Method method = declaredMethods[intSetter];
554+
declaredMethods[intSetter] = declaredMethods[stringSetter];
555+
declaredMethods[stringSetter] = method;
556+
}
557+
super.addProperties(declaredMethods, declaredFields);
558+
}
559+
560+
};
561+
BeanProperty property = bean.getProperties().get("property");
562+
PropertyWithOverloadedSetter target = new PropertyWithOverloadedSetter();
563+
property.setValue(() -> target, "some string");
564+
}
565+
510566
public static class ExampleValueBean {
511567

512568
private int intValue;
@@ -957,4 +1013,22 @@ public void setValue(T value) {
9571013

9581014
}
9591015

1016+
public static class PropertyWithOverloadedSetter {
1017+
1018+
private String property;
1019+
1020+
public void setProperty(int property) {
1021+
this.property = String.valueOf(property);
1022+
}
1023+
1024+
public void setProperty(String property) {
1025+
this.property = property;
1026+
}
1027+
1028+
public String getProperty() {
1029+
return this.property;
1030+
}
1031+
1032+
}
1033+
9601034
}

0 commit comments

Comments
 (0)