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 0b004fa4957..93a9724d442 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 eb3a1568691..bc2faca9962 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 14d671454ba..a41a70e236b 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 @@ -1325,6 +1325,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 @@ -3382,13 +3387,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"; + } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index fc7a9b04b54..91642841999 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -2547,23 +2547,21 @@ class AutowiredAnnotationBeanPostProcessorTests { assertThat(mixedOptionalInjectionBean.nullableBean).isNull(); } - private Consumer methodParameterDeclaredOn( - Class expected) { + + private Consumer methodParameterDeclaredOn(Class expected) { return declaredOn( injectionPoint -> injectionPoint.getMethodParameter().getDeclaringClass(), expected); } - private Consumer fieldDeclaredOn( - Class expected) { + private Consumer fieldDeclaredOn(Class expected) { return declaredOn( injectionPoint -> injectionPoint.getField().getDeclaringClass(), expected); } private Consumer declaredOn( - Function> declaringClassExtractor, - Class expected) { + Function> declaringClassExtractor, Class expected) { return ex -> { InjectionPoint injectionPoint = ex.getInjectionPoint(); Class declaringClass = declaringClassExtractor.apply(injectionPoint); diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index 785044338a2..5e16e11c2a8 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -992,7 +992,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe } /** - * Used to determine whether this listener container currently has more + * Called to determine whether this listener container currently has more * than one idle instance among its scheduled invokers. */ private int getIdleInvokerCount() {