Skip to content

Commit

Permalink
Unwrap nested generic type within FactoryBean target type if necessary
Browse files Browse the repository at this point in the history
Closes gh-29385
  • Loading branch information
jhoeller committed Nov 8, 2022
1 parent f26a7de commit 0e9eab5
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 11 deletions.
@@ -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.
Expand Down Expand Up @@ -102,6 +102,17 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
}
}
}
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) {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -372,8 +374,8 @@ void individualBeanWithFactoryBeanSupplier() {
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);
}
Expand All @@ -388,25 +390,55 @@ void individualBeanWithFactoryBeanSupplierAndTargetType() {
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
Expand Down Expand Up @@ -630,6 +662,36 @@ public boolean isSingleton() {
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 {
Expand Down

0 comments on commit 0e9eab5

Please sign in to comment.