From f3b50c9a58183c26530ed5066d7327731002965b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 5 Mar 2025 22:44:09 +0100 Subject: [PATCH 1/2] Ignore null beans in SimpleAutowireCandidateResolver.resolveAutowireCandidates Closes gh-34543 --- .../factory/support/SimpleAutowireCandidateResolver.java | 6 +++++- .../AutowiredAnnotationBeanPostProcessorTests.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java index b67fdb6e54..12041b3686 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleAutowireCandidateResolver.java @@ -66,11 +66,15 @@ public class SimpleAutowireCandidateResolver implements AutowireCandidateResolve * @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate() * @see AbstractBeanDefinition#isDefaultCandidate() */ + @SuppressWarnings("unchecked") public static Map resolveAutowireCandidates(ConfigurableListableBeanFactory lbf, Class type) { Map candidates = new LinkedHashMap<>(); for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(lbf, type)) { if (AutowireUtils.isAutowireCandidate(lbf, beanName)) { - candidates.put(beanName, lbf.getBean(beanName, type)); + Object beanInstance = lbf.getBean(beanName); + if (!(beanInstance instanceof NullBean)) { + candidates.put(beanName, (T) beanInstance); + } } } return candidates; 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 9c4283a529..f53f9ff5a4 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 @@ -1770,6 +1770,10 @@ class AutowiredAnnotationBeanPostProcessorTests { parent.registerBeanDefinition("testBean4", tb4); bf.setParentBeanFactory(parent); + RootBeanDefinition tb5 = new RootBeanDefinition(NullFactoryMethods.class); + tb5.setFactoryMethodName("createTestBean"); + bf.registerBeanDefinition("testBean5", tb5); + ObjectProviderInjectionBean bean = bf.getBean("annotatedBean", ObjectProviderInjectionBean.class); assertThat(bean.streamTestBeans()).containsExactly(bf.getBean("testBean1", TestBean.class), bf.getBean("testBean2", TestBean.class)); @@ -1789,7 +1793,7 @@ class AutowiredAnnotationBeanPostProcessorTests { Map typeMatches = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, TestBean.class); assertThat(typeMatches.remove("testBean3")).isNotNull(); Map candidates = SimpleAutowireCandidateResolver.resolveAutowireCandidates(bf, TestBean.class); - assertThat(candidates).containsExactlyEntriesOf(candidates); + assertThat(candidates).containsExactlyEntriesOf(typeMatches); } @Test From 2472126475c70c0d5e454ed5734661bdbbe670f3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 5 Mar 2025 22:45:22 +0100 Subject: [PATCH 2/2] Add fallback to pre-6.2.3 behavior for unresolvable generics Closes gh-34535 --- .../support/GenericConversionService.java | 15 +++++++++++++ .../GenericConversionServiceTests.java | 21 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 1fdd9f286e..6a8b3f6bde 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -348,6 +348,13 @@ public class GenericConversionService implements ConfigurableConversionService { conditionalConverter.matches(sourceType, targetType); } + public boolean matchesFallback(TypeDescriptor sourceType, TypeDescriptor targetType) { + return (this.typeInfo.getTargetType() == targetType.getObjectType() && + this.targetType.hasUnresolvableGenerics() && + (!(this.converter instanceof ConditionalConverter conditionalConverter) || + conditionalConverter.matches(sourceType, targetType))); + } + @Override @Nullable public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { @@ -622,12 +629,20 @@ public class GenericConversionService implements ConfigurableConversionService { @Nullable public GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { + // Look for proper match among all converters (taking full generics into account) for (GenericConverter converter : this.converters) { if (!(converter instanceof ConditionalGenericConverter genericConverter) || genericConverter.matches(sourceType, targetType)) { return converter; } } + // Fallback to pre-6.2.3 behavior: accept Class match for unresolvable generics + for (GenericConverter converter : this.converters) { + if (converter instanceof ConverterAdapter converterAdapter && + converterAdapter.matchesFallback(sourceType, targetType)) { + return converter; + } + } return null; } diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index b5155635dc..6fe83b0aaa 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -581,6 +581,18 @@ class GenericConversionServiceTests { assertThat(bList).allMatch(e -> e instanceof BRaw); } + @Test + void stringToListOfMapConverterWithFallbackMatch() { + conversionService.addConverter(new StringToListOfMapConverter()); + + List> result = (List>) conversionService.convert("foo", + TypeDescriptor.valueOf(String.class), + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Map.class)) + ); + + assertThat("foo").isEqualTo(result.get(0).get("bar")); + } + @ExampleAnnotation(active = true) public String annotatedString; @@ -971,4 +983,13 @@ class GenericConversionServiceTests { } } + + private static class StringToListOfMapConverter implements Converter>> { + + @Override + public List> convert(String source) { + return List.of(Map.of("bar", source)); + } + } + }