Skip to content

Commit 0e9eab5

Browse files
committed
Unwrap nested generic type within FactoryBean target type if necessary
Closes gh-29385
1 parent f26a7de commit 0e9eab5

File tree

2 files changed

+84
-11
lines changed

2 files changed

+84
-11
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -102,6 +102,17 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
102102
}
103103
}
104104
}
105+
else {
106+
// Pre-existing target type: In case of a generic FactoryBean type,
107+
// unwrap nested generic type when matching a non-FactoryBean type.
108+
Class<?> resolvedClass = targetType.resolve();
109+
if (resolvedClass != null && FactoryBean.class.isAssignableFrom(resolvedClass)) {
110+
Class<?> typeToBeMatched = dependencyType.resolve();
111+
if (typeToBeMatched != null && !FactoryBean.class.isAssignableFrom(typeToBeMatched)) {
112+
targetType = targetType.getGeneric();
113+
}
114+
}
115+
}
105116
}
106117

107118
if (targetType == null) {

spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.context.annotation;
1818

19+
import java.util.Collections;
1920
import java.util.Map;
21+
import java.util.Set;
2022
import java.util.regex.Pattern;
2123

2224
import org.junit.jupiter.api.Test;
@@ -372,8 +374,8 @@ void individualBeanWithFactoryBeanSupplier() {
372374
context.registerBean("fb", NonInstantiatedFactoryBean.class, NonInstantiatedFactoryBean::new, bd -> bd.setLazyInit(true));
373375
context.refresh();
374376

375-
assertThat(context.getType("fb")).isEqualTo(String.class);
376377
assertThat(context.getType("&fb")).isEqualTo(NonInstantiatedFactoryBean.class);
378+
assertThat(context.getType("fb")).isEqualTo(String.class);
377379
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
378380
assertThat(context.getBeanNamesForType(NonInstantiatedFactoryBean.class)).hasSize(1);
379381
}
@@ -388,25 +390,55 @@ void individualBeanWithFactoryBeanSupplierAndTargetType() {
388390
context.registerBeanDefinition("fb", bd);
389391
context.refresh();
390392

391-
assertThat(context.getType("fb")).isEqualTo(String.class);
392393
assertThat(context.getType("&fb")).isEqualTo(FactoryBean.class);
394+
assertThat(context.getType("fb")).isEqualTo(String.class);
393395
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
394396
assertThat(context.getBeanNamesForType(NonInstantiatedFactoryBean.class)).isEmpty();
395397
}
396398

399+
@Test
400+
void individualBeanWithFactoryBeanTypeAsTargetType() {
401+
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
402+
RootBeanDefinition bd1 = new RootBeanDefinition();
403+
bd1.setBeanClass(SetFactoryBean.class);
404+
bd1.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.class, String.class)));
405+
bd1.setLazyInit(true);
406+
context.registerBeanDefinition("fb1", bd1);
407+
RootBeanDefinition bd2 = new RootBeanDefinition();
408+
bd2.setBeanClass(UntypedFactoryBean.class);
409+
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(Set.class, Integer.class)));
410+
bd2.setLazyInit(true);
411+
context.registerBeanDefinition("fb2", bd2);
412+
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
413+
context.refresh();
414+
415+
assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class);
416+
assertThat(context.getType("fb1")).isEqualTo(Set.class);
417+
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
418+
assertThat(context.getBeanNamesForType(SetFactoryBean.class)).hasSize(1);
419+
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
420+
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
421+
}
422+
397423
@Test
398424
void individualBeanWithFactoryBeanObjectTypeAsTargetType() {
399425
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
400-
RootBeanDefinition bd = new RootBeanDefinition();
401-
bd.setBeanClass(TypedFactoryBean.class);
402-
bd.setTargetType(String.class);
403-
context.registerBeanDefinition("fb", bd);
426+
RootBeanDefinition bd1 = new RootBeanDefinition();
427+
bd1.setBeanClass(SetFactoryBean.class);
428+
bd1.setTargetType(ResolvableType.forClassWithGenerics(Set.class, String.class));
429+
context.registerBeanDefinition("fb1", bd1);
430+
RootBeanDefinition bd2 = new RootBeanDefinition();
431+
bd2.setBeanClass(UntypedFactoryBean.class);
432+
bd2.setTargetType(ResolvableType.forClassWithGenerics(Set.class, Integer.class));
433+
context.registerBeanDefinition("fb2", bd2);
434+
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
404435
context.refresh();
405436

406-
assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class);
407-
assertThat(context.getType("fb")).isEqualTo(String.class);
408-
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(1);
409-
assertThat(context.getBeanNamesForType(TypedFactoryBean.class)).hasSize(1);
437+
assertThat(context.getType("&fb1")).isEqualTo(SetFactoryBean.class);
438+
assertThat(context.getType("fb1")).isEqualTo(Set.class);
439+
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
440+
assertThat(context.getBeanNamesForType(SetFactoryBean.class)).hasSize(1);
441+
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
410442
}
411443

412444
@Test
@@ -630,6 +662,36 @@ public boolean isSingleton() {
630662
return false;
631663
}
632664
}
665+
666+
static class SetFactoryBean implements FactoryBean<Set<String>> {
667+
668+
@Override
669+
public Set<String> getObject() {
670+
return Collections.emptySet();
671+
}
672+
673+
@Override
674+
public Class<?> getObjectType() {
675+
return Set.class;
676+
}
677+
678+
@Override
679+
public boolean isSingleton() {
680+
return true;
681+
}
682+
}
683+
684+
static class FactoryResultInjectionPoint {
685+
686+
@Autowired
687+
Set<String> factoryResult;
688+
}
689+
690+
static class FactoryBeanInjectionPoints extends FactoryResultInjectionPoint {
691+
692+
@Autowired
693+
FactoryBean<Set<String>> factoryBean;
694+
}
633695
}
634696

635697
class TestBean {

0 commit comments

Comments
 (0)