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);
|
ph.setValue(valueToApply);
|
||||||
}
|
}
|
||||||
catch (TypeMismatchException ex) {
|
catch (TypeMismatchException ex) {
|
||||||
throw ex;
|
if (!ph.setValueFallbackIfPossible(pv.getValue())) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (InvocationTargetException ex) {
|
catch (InvocationTargetException ex) {
|
||||||
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
|
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
|
||||||
|
@ -1061,6 +1063,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
|
||||||
public abstract Object getValue() throws Exception;
|
public abstract Object getValue() throws Exception;
|
||||||
|
|
||||||
public abstract void setValue(@Nullable Object value) 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.beans.PropertyDescriptor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
@ -278,6 +280,22 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements
|
||||||
ReflectionUtils.makeAccessible(writeMethod);
|
ReflectionUtils.makeAccessible(writeMethod);
|
||||||
writeMethod.invoke(getWrappedInstance(), value);
|
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;
|
private final Method writeMethod;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile Set<Method> ambiguousWriteMethods;
|
private Set<Method> ambiguousWriteMethods;
|
||||||
|
|
||||||
|
private volatile boolean ambiguousWriteMethodsLogged;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private MethodParameter writeMethodParameter;
|
private MethodParameter writeMethodParameter;
|
||||||
|
@ -147,16 +149,28 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
|
||||||
|
|
||||||
public Method getWriteMethodForActualAccess() {
|
public Method getWriteMethodForActualAccess() {
|
||||||
Assert.state(this.writeMethod != null, "No write method available");
|
Assert.state(this.writeMethod != null, "No write method available");
|
||||||
Set<Method> ambiguousCandidates = this.ambiguousWriteMethods;
|
if (this.ambiguousWriteMethods != null && !this.ambiguousWriteMethodsLogged) {
|
||||||
if (ambiguousCandidates != null) {
|
this.ambiguousWriteMethodsLogged = true;
|
||||||
this.ambiguousWriteMethods = null;
|
|
||||||
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).debug("Non-unique JavaBean property '" +
|
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).debug("Non-unique JavaBean property '" +
|
||||||
getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
|
getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
|
||||||
this.writeMethod + "]: " + ambiguousCandidates);
|
this.writeMethod + "]: " + this.ambiguousWriteMethods);
|
||||||
}
|
}
|
||||||
return this.writeMethod;
|
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() {
|
public MethodParameter getWriteMethodParameter() {
|
||||||
Assert.state(this.writeMethodParameter != null, "No write method available");
|
Assert.state(this.writeMethodParameter != null, "No write method available");
|
||||||
return this.writeMethodParameter;
|
return this.writeMethodParameter;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.beans;
|
package org.springframework.beans;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -172,10 +173,26 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||||
void setterOverload() {
|
void setterOverload() {
|
||||||
SetterOverload target = new SetterOverload();
|
SetterOverload target = new SetterOverload();
|
||||||
BeanWrapper accessor = createAccessor(target);
|
BeanWrapper accessor = createAccessor(target);
|
||||||
|
|
||||||
accessor.setPropertyValue("object", "a String");
|
accessor.setPropertyValue("object", "a String");
|
||||||
assertThat(target.value).isEqualTo("a String");
|
assertThat(target.value).isEqualTo("a String");
|
||||||
assertThat(target.getObject()).isEqualTo("a String");
|
assertThat(target.getObject()).isEqualTo("a String");
|
||||||
assertThat(accessor.getPropertyValue("object")).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
|
@Test
|
||||||
|
@ -382,7 +399,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||||
public String value;
|
public String value;
|
||||||
|
|
||||||
public void setObject(Integer length) {
|
public void setObject(Integer length) {
|
||||||
this.value = length.toString();
|
this.value = length + "i";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setObject(String object) {
|
public void setObject(String object) {
|
||||||
|
@ -392,6 +409,14 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests {
|
||||||
public String getObject() {
|
public String getObject() {
|
||||||
return this.value;
|
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