Unwrap nested generic type within FactoryBean target type if necessary
Closes gh-29385
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -102,6 +102,17 @@ public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCan
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Pre-existing target type: In case of a generic FactoryBean type,
|
||||
// unwrap nested generic type when matching a non-FactoryBean type.
|
||||
Class<?> resolvedClass = targetType.resolve();
|
||||
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
|
||||
Class<?> typeToBeMatched = dependencyType.resolve();
|
||||
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
|
||||
targetType = targetType.getGeneric();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetType == null) {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
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;
|
||||
@@ -372,8 +374,8 @@ class AnnotationConfigApplicationContextTests {
|
||||
context.registerBean("fb", NonInstantiatedFactoryBean.class, NonInstantiatedFactoryBean::new, bd -> bd.setLazyInit(true));
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getType("fb")).isEqualTo(String.class);
|
||||
assertThat(context.getType("&fb")).isEqualTo(NonInstantiatedFactoryBean.class);
|
||||
assertThat(context.getType("fb")).isEqualTo(String.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(NonInstantiatedFactoryBean.class)).hasSize(1);
|
||||
}
|
||||
@@ -388,25 +390,55 @@ class AnnotationConfigApplicationContextTests {
|
||||
context.registerBeanDefinition("fb", bd);
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getType("fb")).isEqualTo(String.class);
|
||||
assertThat(context.getType("&fb")).isEqualTo(FactoryBean.class);
|
||||
assertThat(context.getType("fb")).isEqualTo(String.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(NonInstantiatedFactoryBean.class)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void individualBeanWithFactoryBeanObjectTypeAsTargetType() {
|
||||
void individualBeanWithFactoryBeanTypeAsTargetType() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
RootBeanDefinition bd = new RootBeanDefinition();
|
||||
bd.setBeanClass(TypedFactoryBean.class);
|
||||
bd.setTargetType(String.class);
|
||||
context.registerBeanDefinition("fb", bd);
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition();
|
||||
bd1.setBeanClass(SetFactoryBean.class);
|
||||
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.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.setLazyInit(true);
|
||||
context.registerBeanDefinition("fb2", bd2);
|
||||
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
|
||||
context.refresh();
|
||||
|
||||
assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class);
|
||||
assertThat(context.getType("fb")).isEqualTo(String.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBeanNamesForType(TypedFactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class);
|
||||
assertThat(context.getType("fb1")).isEqualTo(Set.class);
|
||||
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(SetFactoryBean.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 individualBeanWithFactoryBeanObjectTypeAsTargetType() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
RootBeanDefinition bd1 = new RootBeanDefinition();
|
||||
bd1.setBeanClass(SetFactoryBean.class);
|
||||
bd1.setTargetType(ResolvableType.forClassWithGenerics(Set.class, String.class));
|
||||
context.registerBeanDefinition("fb1", bd1);
|
||||
RootBeanDefinition bd2 = new RootBeanDefinition();
|
||||
bd2.setBeanClass(UntypedFactoryBean.class);
|
||||
bd2.setTargetType(ResolvableType.forClassWithGenerics(Set.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.getBeanNamesForType(FactoryBean.class)).hasSize(2);
|
||||
assertThat(context.getBeanNamesForType(SetFactoryBean.class)).hasSize(1);
|
||||
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -630,6 +662,36 @@ class AnnotationConfigApplicationContextTests {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class SetFactoryBean implements FactoryBean<Set<String>> {
|
||||
|
||||
@Override
|
||||
public Set<String> getObject() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Set.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class FactoryResultInjectionPoint {
|
||||
|
||||
@Autowired
|
||||
Set<String> factoryResult;
|
||||
}
|
||||
|
||||
static class FactoryBeanInjectionPoints extends FactoryResultInjectionPoint {
|
||||
|
||||
@Autowired
|
||||
FactoryBean<Set<String>> factoryBean;
|
||||
}
|
||||
}
|
||||
|
||||
class TestBean {
|
||||
|
||||
Reference in New Issue
Block a user