Skip to content

Commit

Permalink
Introduce write method fallback step for overloaded setter methods
Browse files Browse the repository at this point in the history
Closes gh-31872
  • Loading branch information
jhoeller committed Dec 21, 2023
1 parent 85cb6cc commit bd65a19
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,9 @@ private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv)
ph.setValue(valueToApply);
}
catch (TypeMismatchException ex) {
throw ex;
if (!ph.setValueFallbackIfPossible(pv.getValue())) {
throw ex;
}
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
Expand Down Expand Up @@ -1061,6 +1063,10 @@ public TypeDescriptor getCollectionType(int nestingLevel) {
public abstract Object getValue() throws Exception;

public abstract void setValue(@Nullable Object value) throws Exception;

public boolean setValueFallbackIfPossible(@Nullable Object value) {
return false;
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
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;
Expand Down Expand Up @@ -278,6 +280,22 @@ public void setValue(@Nullable Object value) throws Exception {
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;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -147,16 +149,28 @@ public Method getWriteMethod() {

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.springframework.beans;

import java.time.Duration;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -172,10 +173,26 @@ void setPropertyTypeMismatch() {
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
Expand Down Expand Up @@ -382,7 +399,7 @@ public static class SetterOverload {
public String value;

public void setObject(Integer length) {
this.value = length.toString();
this.value = length + "i";
}

public void setObject(String object) {
Expand All @@ -392,6 +409,14 @@ public void setObject(String object) {
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";
}
}


Expand Down

0 comments on commit bd65a19

Please sign in to comment.