diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 0b004fa495..93a9724d44 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import org.apache.commons.logging.LogFactory; +import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; @@ -283,16 +284,25 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements @Override public boolean setValueFallbackIfPossible(@Nullable Object value) { - Method writeMethod = this.pd.getWriteMethodFallback(value != null ? value.getClass() : null); - if (writeMethod != null) { - ReflectionUtils.makeAccessible(writeMethod); - try { + try { + Method writeMethod = this.pd.getWriteMethodFallback(value != null ? value.getClass() : null); + if (writeMethod == null) { + writeMethod = this.pd.getUniqueWriteMethodFallback(); + if (writeMethod != null) { + // Conversion necessary as we would otherwise have received the method + // from the type-matching getWriteMethodFallback call above already + value = convertForProperty(this.pd.getName(), null, value, + new TypeDescriptor(new MethodParameter(writeMethod, 0))); + } + } + if (writeMethod != null) { + ReflectionUtils.makeAccessible(writeMethod); writeMethod.invoke(getWrappedInstance(), value); return true; } - catch (Exception ex) { - LogFactory.getLog(BeanPropertyHandler.class).debug("Write method fallback failed", ex); - } + } + catch (Exception ex) { + LogFactory.getLog(BeanPropertyHandler.class).debug("Write method fallback failed", ex); } return false; } diff --git a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java index eb3a156869..bc2faca996 100644 --- a/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/GenericTypeAwarePropertyDescriptor.java @@ -171,6 +171,14 @@ final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor { return null; } + @Nullable + public Method getUniqueWriteMethodFallback() { + if (this.ambiguousWriteMethods != null && this.ambiguousWriteMethods.size() == 1) { + return this.ambiguousWriteMethods.iterator().next(); + } + return null; + } + public boolean hasUniqueWriteMethod() { return (this.writeMethod != null && this.ambiguousWriteMethods == null); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 501311bd21..ee7dfcd45a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1342,6 +1342,11 @@ class DefaultListableBeanFactoryTests { rbd.getPropertyValues().add("value", Duration.ofSeconds(1000)); lbf.registerBeanDefinition("overloaded", rbd); assertThat(lbf.getBean(SetterOverload.class).getObject()).isEqualTo("1000s"); + + rbd = new RootBeanDefinition(SetterOverload.class); + rbd.getPropertyValues().add("value", "1000"); + lbf.registerBeanDefinition("overloaded", rbd); + assertThat(lbf.getBean(SetterOverload.class).getObject()).isEqualTo("1000i"); } @Test @@ -3369,13 +3374,13 @@ class DefaultListableBeanFactoryTests { return this.value; } - public void setValue(int length) { - this.value = length + "i"; - } - public void setValue(Duration duration) { this.value = duration.getSeconds() + "s"; } + + public void setValue(int length) { + this.value = length + "i"; + } }