Browse Source

Unwrap nested generic type within FactoryBean target type if necessary

Closes gh-29385
pull/27328/merge
Juergen Hoeller 3 years ago
parent
commit
0e9eab55ce
  1. 13
      spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
  2. 82
      spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java

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

@ -1,5 +1,5 @@ @@ -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 @@ -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) {

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

@ -16,7 +16,9 @@ @@ -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 { @@ -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 { @@ -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 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.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("&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 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(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("&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", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
}
@Test
@ -630,6 +662,36 @@ class AnnotationConfigApplicationContextTests { @@ -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 {

Loading…
Cancel
Save