From 70bb785ed61d2b367e1a61a84a38e82c3281b60d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 9 Nov 2022 11:24:50 +0100 Subject: [PATCH] Turn nested generic FactoryBean type into resolved Class for fallback match See gh-29385 --- ...ricTypeAwareAutowireCandidateResolver.java | 5 ++ ...notationConfigApplicationContextTests.java | 62 +++++++++++++------ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index e8e14fd81c8c..960f86b7fdb1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java @@ -110,6 +110,11 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc Class typeToBeMatched = dependencyType.resolve(); if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) { targetType = targetType.getGeneric(); + if (descriptor.fallbackMatchAllowed()) { + // Matching the Class-based type determination for FactoryBean + // objects in the lazy-determination getType code path below. + targetType = ResolvableType.forClass(targetType.resolve()); + } } } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java index d11db0c30672..bb107dd7bd16 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java @@ -16,9 +16,7 @@ package org.springframework.context.annotation; -import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.regex.Pattern; import org.junit.jupiter.api.Test; @@ -400,44 +398,66 @@ void individualBeanWithFactoryBeanSupplierAndTargetType() { void individualBeanWithFactoryBeanTypeAsTargetType() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); RootBeanDefinition bd1 = new RootBeanDefinition(); - bd1.setBeanClass(SetFactoryBean.class); - bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.class, String.class))); + bd1.setBeanClass(GenericHolderFactoryBean.class); + bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class))); bd1.setLazyInit(true); context.registerBeanDefinition("fb1", bd1); RootBeanDefinition bd2 = new RootBeanDefinition(); bd2.setBeanClass(UntypedFactoryBean.class); - bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.class, Integer.class))); + bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class))); bd2.setLazyInit(true); context.registerBeanDefinition("fb2", bd2); context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class)); context.refresh(); - assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class); - assertThat(context.getType("fb1")).isEqualTo(Set.class); + assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class); + assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class); assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2); - assertThat(context.getBeanNamesForType(SetFactoryBean.class)).hasSize(1); + assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1); assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1")); assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1")); } + @Test + void individualBeanWithUnresolvedFactoryBeanTypeAsTargetType() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + RootBeanDefinition bd1 = new RootBeanDefinition(); + bd1.setBeanClass(GenericHolderFactoryBean.class); + bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Object.class))); + bd1.setLazyInit(true); + context.registerBeanDefinition("fb1", bd1); + RootBeanDefinition bd2 = new RootBeanDefinition(); + bd2.setBeanClass(UntypedFactoryBean.class); + bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class))); + bd2.setLazyInit(true); + context.registerBeanDefinition("fb2", bd2); + context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class)); + context.refresh(); + + assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class); + assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class); + assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2); + assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1")); + } + @Test void individualBeanWithFactoryBeanObjectTypeAsTargetType() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); RootBeanDefinition bd1 = new RootBeanDefinition(); - bd1.setBeanClass(SetFactoryBean.class); - bd1.setTargetType(ResolvableType.forClassWithGenerics(Set.class, String.class)); + bd1.setBeanClass(GenericHolderFactoryBean.class); + bd1.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)); context.registerBeanDefinition("fb1", bd1); RootBeanDefinition bd2 = new RootBeanDefinition(); bd2.setBeanClass(UntypedFactoryBean.class); - bd2.setTargetType(ResolvableType.forClassWithGenerics(Set.class, Integer.class)); + bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)); context.registerBeanDefinition("fb2", bd2); context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class)); context.refresh(); - assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class); - assertThat(context.getType("fb1")).isEqualTo(Set.class); + assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class); + assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class); assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2); - assertThat(context.getBeanNamesForType(SetFactoryBean.class)).hasSize(1); + assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1); assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1")); } @@ -663,16 +683,18 @@ public boolean isSingleton() { } } - static class SetFactoryBean implements FactoryBean> { + static class GenericHolder {} + + static class GenericHolderFactoryBean implements FactoryBean> { @Override - public Set getObject() { - return Collections.emptySet(); + public GenericHolder getObject() { + return new GenericHolder<>(); } @Override public Class getObjectType() { - return Set.class; + return GenericHolder.class; } @Override @@ -684,13 +706,13 @@ public boolean isSingleton() { static class FactoryResultInjectionPoint { @Autowired - Set factoryResult; + GenericHolder factoryResult; } static class FactoryBeanInjectionPoints extends FactoryResultInjectionPoint { @Autowired - FactoryBean> factoryBean; + FactoryBean> factoryBean; } }