Introduce write method fallback step for overloaded setter methods
Closes gh-31872
This commit is contained in:
parent
85cb6cc5fb
commit
bd65a19d71
|
@ -461,7 +461,9 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
|||
ph.setValue(valueToApply);
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
throw ex;
|
||||
if (!ph.setValueFallbackIfPossible(pv.getValue())) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
|
||||
|
@ -1061,6 +1063,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
|||
public abstract Object getValue() throws Exception;
|
||||
|
||||
public abstract void setValue(@Nullable Object value) throws Exception;
|
||||
|
||||
public boolean setValueFallbackIfPossible(@Nullable Object value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.springframework.beans;
|
|||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -278,6 +280,22 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
|
|||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
writeMethod.invoke(getWrappedInstance(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setValueFallbackIfPossible(@Nullable Object value) {
|
||||
Method writeMethod = this.pd.getWriteMethodFallback(value != null ? value.getClass() : null);
|
||||
if (writeMethod != null) {
|
||||
ReflectionUtils.makeAccessible(writeMethod);
|
||||
try {
|
||||
writeMethod.invoke(getWrappedInstance(), value);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
LogFactory.getLog(BeanPropertyHandler.class).debug("Write method fallback failed", ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,9 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
|||
private final Method writeMethod;
|
||||
|
||||
@Nullable
|
||||
private volatile Set<Method> ambiguousWriteMethods;
|
||||
private Set<Method> ambiguousWriteMethods;
|
||||
|
||||
private volatile boolean ambiguousWriteMethodsLogged;
|
||||
|
||||
@Nullable
|
||||
private MethodParameter writeMethodParameter;
|
||||
|
@ -147,16 +149,28 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
|||
|
||||
public Method getWriteMethodForActualAccess() {
|
||||
Assert.state(this.writeMethod != null, "No write method available");
|
||||
Set<Method> ambiguousCandidates = this.ambiguousWriteMethods;
|
||||
if (ambiguousCandidates != null) {
|
||||
this.ambiguousWriteMethods = null;
|
||||
if (this.ambiguousWriteMethods != null && !this.ambiguousWriteMethodsLogged) {
|
||||
this.ambiguousWriteMethodsLogged = true;
|
||||
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).debug("Non-unique JavaBean property '" +
|
||||
getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
|
||||
this.writeMethod + "]: " + ambiguousCandidates);
|
||||
this.writeMethod + "]: " + this.ambiguousWriteMethods);
|
||||
}
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Method getWriteMethodFallback(@Nullable Class<?> valueType) {
|
||||
if (this.ambiguousWriteMethods != null) {
|
||||
for (Method method : this.ambiguousWriteMethods) {
|
||||
Class<?> paramType = method.getParameterTypes()[0];
|
||||
if (valueType != null ? paramType.isAssignableFrom(valueType) : !paramType.isPrimitive()) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodParameter getWriteMethodParameter() {
|
||||
Assert.state(this.writeMethodParameter != null, "No write method available");
|
||||
return this.writeMethodParameter;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.beans;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
@ -172,10 +173,26 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
|||
void setterOverload() {
|
||||
SetterOverload target = new SetterOverload();
|
||||
BeanWrapper accessor = createAccessor(target);
|
||||
|
||||
accessor.setPropertyValue("object", "a String");
|
||||
assertThat(target.value).isEqualTo("a String");
|
||||
assertThat(target.getObject()).isEqualTo("a String");
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo("a String");
|
||||
|
||||
accessor.setPropertyValue("object", 1000);
|
||||
assertThat(target.value).isEqualTo("1000");
|
||||
assertThat(target.getObject()).isEqualTo("1000");
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo("1000");
|
||||
|
||||
accessor.setPropertyValue("value", 1000);
|
||||
assertThat(target.value).isEqualTo("1000i");
|
||||
assertThat(target.getObject()).isEqualTo("1000i");
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo("1000i");
|
||||
|
||||
accessor.setPropertyValue("value", Duration.ofSeconds(1000));
|
||||
assertThat(target.value).isEqualTo("1000s");
|
||||
assertThat(target.getObject()).isEqualTo("1000s");
|
||||
assertThat(accessor.getPropertyValue("object")).isEqualTo("1000s");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -382,7 +399,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
|||
public String value;
|
||||
|
||||
public void setObject(Integer length) {
|
||||
this.value = length.toString();
|
||||
this.value = length + "i";
|
||||
}
|
||||
|
||||
public void setObject(String object) {
|
||||
|
@ -392,6 +409,14 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
|||
public String getObject() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(int length) {
|
||||
this.value = length + "i";
|
||||
}
|
||||
|
||||
public void setValue(Duration duration) {
|
||||
this.value = duration.getSeconds() + "s";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue