Skip to content

Commit

Permalink
Revised tests for generic FactoryBean type matching (backported)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhoeller committed Apr 8, 2024
1 parent a0bd13c commit 9412d78
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.util.Set;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;

import org.springframework.beans.factory.BeanCreationException;
Expand Down Expand Up @@ -756,25 +758,31 @@ void genericMatchingWithBeanNameDifferentiation() {
assertThat(floatStoreNames).isEmpty();
}

@Test
void genericMatchingWithFullTypeDifferentiation() {
@ParameterizedTest
@ValueSource(classes = {NumberStoreFactory.class, NumberStoreFactoryBeans.class})
void genericMatchingWithFullTypeDifferentiation(Class<?> factoryClass) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());

RootBeanDefinition bd1 = new RootBeanDefinition(NumberStoreFactory.class);
RootBeanDefinition bd1 = new RootBeanDefinition(factoryClass);
bd1.setFactoryMethodName("newDoubleStore");
bf.registerBeanDefinition("store1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition(NumberStoreFactory.class);
RootBeanDefinition bd2 = new RootBeanDefinition(factoryClass);
bd2.setFactoryMethodName("newFloatStore");
bf.registerBeanDefinition("store2", bd2);
bf.registerBeanDefinition("numberBean",
new RootBeanDefinition(NumberBean.class, RootBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
RootBeanDefinition bd3 = new RootBeanDefinition(NumberBean.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bd3.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
bf.registerBeanDefinition("numberBean", bd3);

NumberBean nb = bf.getBean(NumberBean.class);
NumberStore<?> store1 = bf.getBean("store1", NumberStore.class);
assertThat(nb.getDoubleStore()).isSameAs(store1);
NumberStore<?> store2 = bf.getBean("store2", NumberStore.class);
NumberBean nb = bf.getBean(NumberBean.class);
assertThat(nb.getDoubleStore()).isSameAs(store1);
assertThat(nb.getFloatStore()).isSameAs(store2);
nb = bf.getBean(NumberBean.class);
assertThat(nb.getDoubleStore()).isSameAs(store1);
assertThat(nb.getFloatStore()).isSameAs(store2);

String[] numberStoreNames = bf.getBeanNamesForType(ResolvableType.forClass(NumberStore.class));
Expand Down Expand Up @@ -822,16 +830,17 @@ void genericMatchingWithFullTypeDifferentiation() {
assertThat(floatStoreProvider.orderedStream()).singleElement().isEqualTo(store2);
}

@Test
void genericMatchingWithUnresolvedOrderedStream() {
@ParameterizedTest
@ValueSource(classes = {NumberStoreFactory.class, NumberStoreFactoryBeans.class})
void genericMatchingWithUnresolvedOrderedStream(Class<?> factoryClass) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());

RootBeanDefinition bd1 = new RootBeanDefinition(NumberStoreFactory.class);
RootBeanDefinition bd1 = new RootBeanDefinition(factoryClass);
bd1.setFactoryMethodName("newDoubleStore");
bf.registerBeanDefinition("store1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition(NumberStoreFactory.class);
RootBeanDefinition bd2 = new RootBeanDefinition(factoryClass);
bd2.setFactoryMethodName("newFloatStore");
bf.registerBeanDefinition("store2", bd2);

Expand All @@ -854,7 +863,22 @@ void genericMatchingAgainstFactoryBeanClass() {
bf.registerBeanDefinition("myFactoryBeanHolder",
new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));

assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).contains(bf.getBean(MyFactoryBean.class));
assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).containsOnly(bf.getBean(MyFactoryBean.class));
}

@Test // gh-32489
void genericMatchingAgainstLazyFactoryBeanClass() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());

RootBeanDefinition bd = new RootBeanDefinition(MyFactoryBean.class);
bd.setTargetType(ResolvableType.forClassWithGenerics(MyFactoryBean.class, String.class));
bd.setLazyInit(true);
bf.registerBeanDefinition("myFactoryBean", bd);
bf.registerBeanDefinition("myFactoryBeanHolder",
new RootBeanDefinition(MyFactoryBeanHolder.class, AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));

assertThat(bf.getBean(MyFactoryBeanHolder.class).factoryBeans).containsOnly(bf.getBean(MyFactoryBean.class));
}


Expand Down Expand Up @@ -974,6 +998,38 @@ public static NumberStore<Float> newFloatStore() {
}


public static class NumberStoreFactoryBeans {

@Order(1)
public static FactoryBean<NumberStore<Double>> newDoubleStore() {
return new FactoryBean<>() {
@Override
public NumberStore<Double> getObject() {
return new DoubleStore();
}
@Override
public Class<?> getObjectType() {
return DoubleStore.class;
}
};
}

@Order(0)
public static FactoryBean<NumberStore<Float>> newFloatStore() {
return new FactoryBean<>() {
@Override
public NumberStore<Float> getObject() {
return new FloatStore();
}
@Override
public Class<?> getObjectType() {
return FloatStore.class;
}
};
}
}


public interface MyGenericInterfaceForFactoryBeans<T> {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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 @@ -307,8 +307,8 @@ void individualBeanWithNullReturningSupplier() {
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue();

assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
assertThat(context.getBeansOfType(BeanB.class).values()).singleElement().isSameAs(context.getBean(BeanB.class));
assertThat(context.getBeansOfType(BeanC.class).values()).singleElement().isSameAs(context.getBean(BeanC.class));

assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> context.getBeanFactory().resolveNamedBean(BeanA.class));
Expand Down Expand Up @@ -409,37 +409,43 @@ void individualBeanWithFactoryBeanTypeAsTargetType() {
bd2.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryBeanInjectionPoints.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();

assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryBean).isSameAs(context.getBean("&fb1"));
assertThat(context.getBean("ip", FactoryBeanInjectionPoints.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.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 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.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, ResolvableType.forClassWithGenerics(GenericHolder.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(GenericHolder.class, Integer.class)));
bd2.setLazyInit(true);
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryBeanInjectionPoints.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();

assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
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"));
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
}

@Test
Expand All @@ -453,14 +459,17 @@ void individualBeanWithFactoryBeanObjectTypeAsTargetType() {
bd2.setBeanClass(UntypedFactoryBean.class);
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericHolder.class, Integer.class));
context.registerBeanDefinition("fb2", bd2);
context.registerBeanDefinition("ip", new RootBeanDefinition(FactoryResultInjectionPoint.class));
RootBeanDefinition bd3 = new RootBeanDefinition(FactoryResultInjectionPoint.class);
bd3.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("ip", bd3);
context.refresh();

assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
assertThat(context.getType("&fb1")).isEqualTo(GenericHolderFactoryBean.class);
assertThat(context.getType("fb1")).isEqualTo(GenericHolder.class);
assertThat(context.getBeanNamesForType(FactoryBean.class)).hasSize(2);
assertThat(context.getBeanNamesForType(GenericHolderFactoryBean.class)).hasSize(1);
assertThat(context.getBean("ip", FactoryResultInjectionPoint.class).factoryResult).isSameAs(context.getBean("fb1"));
}

@Test
Expand Down

0 comments on commit 9412d78

Please sign in to comment.