Turn nested generic FactoryBean type into resolved Class for fallback match

See gh-29385
This commit is contained in:
Juergen Hoeller 2022-11-09 11:24:50 +01:00
parent 69736af5fc
commit 70bb785ed6
2 changed files with 47 additions and 20 deletions

View File

@ -110,6 +110,11 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
Class<?> typeToBeMatched = dependencyType.resolve(); Class<?> typeToBeMatched = dependencyType.resolve();
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) { if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
targetType = targetType.getGeneric(); 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());
}
} }
} }
} }

View File

@ -16,9 +16,7 @@
package org.springframework.context.annotation; package org.springframework.context.annotation;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -400,44 +398,66 @@ class AnnotationConfigApplicationContextTests {
void individualBeanWithFactoryBeanTypeAsTargetType() { void individualBeanWithFactoryBeanTypeAsTargetType() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd1 = new RootBeanDefinition(); RootBeanDefinition bd1 = new RootBeanDefinition();
bd1.setBeanClass(SetFactoryBean.class); bd1.setBeanClass(GenericHolderFactoryBean.class);
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.class, String.class))); bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, String.class)));
bd1.setLazyInit(true); bd1.setLazyInit(true);
context.registerBeanDefinition("fb1", bd1); context.registerBeanDefinition("fb1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition(); RootBeanDefinition bd2 = new RootBeanDefinition();
bd2.setBeanClass(UntypedFactoryBean.class); 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); bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2); context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class)); context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
context.refresh(); context.refresh();
assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class); assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(Set.class); assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2); 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).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).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 @Test
void individualBeanWithFactoryBeanObjectTypeAsTargetType() { void individualBeanWithFactoryBeanObjectTypeAsTargetType() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd1 = new RootBeanDefinition(); RootBeanDefinition bd1 = new RootBeanDefinition();
bd1.setBeanClass(SetFactoryBean.class); bd1.setBeanClass(GenericHolderFactoryBean.class);
bd1.setTargetType(ResolvableType.forClassWithGenerics(Set.class, String.class)); bd1.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, String.class));
context.registerBeanDefinition("fb1", bd1); context.registerBeanDefinition("fb1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition(); RootBeanDefinition bd2 = new RootBeanDefinition();
bd2.setBeanClass(UntypedFactoryBean.class); 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("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class)); context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
context.refresh(); context.refresh();
assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class); assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(Set.class); assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2); 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")); assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
} }
@ -663,16 +683,18 @@ class AnnotationConfigApplicationContextTests {
} }
} }
static class SetFactoryBean implements FactoryBean<Set<String>> { static class GenericHolder<T> {}
static class GenericHolderFactoryBean implements FactoryBean<GenericHolder<?>> {
@Override @Override
public Set<String> getObject() { public GenericHolder<?> getObject() {
return Collections.emptySet(); return new GenericHolder<>();
} }
@Override @Override
public Class<?> getObjectType() { public Class<?> getObjectType() {
return Set.class; return GenericHolder.class;
} }
@Override @Override
@ -684,13 +706,13 @@ class AnnotationConfigApplicationContextTests {
static class FactoryResultInjectionPoint { static class FactoryResultInjectionPoint {
@Autowired @Autowired
Set<String> factoryResult; GenericHolder<String> factoryResult;
} }
static class FactoryBeanInjectionPoints extends FactoryResultInjectionPoint { static class FactoryBeanInjectionPoints extends FactoryResultInjectionPoint {
@Autowired @Autowired
FactoryBean<Set<String>> factoryBean; FactoryBean<GenericHolder<String>> factoryBean;
} }
} }