Link factoryMethod consistently in AOT-generated bean definitions

Closes gh-28748
This commit is contained in:
Stephane Nicoll
2022-07-18 09:20:26 +01:00
committed by Phillip Webb
parent f2d31b7a20
commit 75ab47b57c
9 changed files with 506 additions and 320 deletions

View File

@@ -20,6 +20,7 @@ import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -39,7 +40,7 @@ import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.aot.AutowiredInstantiationArgumentsResolverTests.Enclosing.InnerSingleArgConstructor;
import org.springframework.beans.factory.aot.BeanInstanceSupplierTests.Enclosing.InnerSingleArgConstructor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
@@ -54,6 +55,10 @@ import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.function.ThrowingBiFunction;
import org.springframework.util.function.ThrowingFunction;
import org.springframework.util.function.ThrowingSupplier;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -62,19 +67,19 @@ import static org.assertj.core.api.Assertions.entry;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AutowiredInstantiationArgumentsResolver}.
* Tests for {@link BeanInstanceSupplier}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
class AutowiredInstantiationArgumentsResolverTests {
class BeanInstanceSupplierTests {
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@Test
void forConstructorWhenParameterTypesIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AutowiredInstantiationArgumentsResolver
.isThrownBy(() -> BeanInstanceSupplier
.forConstructor((Class<?>[]) null))
.withMessage("'parameterTypes' must not be null");
}
@@ -82,27 +87,33 @@ class AutowiredInstantiationArgumentsResolverTests {
@Test
void forConstructorWhenParameterTypesContainsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AutowiredInstantiationArgumentsResolver
.isThrownBy(() -> BeanInstanceSupplier
.forConstructor(String.class, null))
.withMessage("'parameterTypes' must not contain null elements");
}
@Test
void forConstructorWhenNotFoundThrowsException() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(InputStream.class);
Source source = new Source(SingleArgConstructor.class, resolver);
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.resolve(registerBean)).withMessage(
.isThrownBy(() -> resolver.get(registerBean)).withMessage(
"Constructor with parameter types [java.io.InputStream] cannot be found on "
+ SingleArgConstructor.class.getName());
}
@Test
void forConstructorReturnsNullFactoryMethod() {
BeanInstanceSupplier resolver = BeanInstanceSupplier.forConstructor(String.class);
assertThat(resolver.getFactoryMethod()).isNull();
}
@Test
void forFactoryMethodWhenDeclaringClassIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AutowiredInstantiationArgumentsResolver
.isThrownBy(() -> BeanInstanceSupplier
.forFactoryMethod(null, "test"))
.withMessage("'declaringClass' must not be null");
}
@@ -110,7 +121,7 @@ class AutowiredInstantiationArgumentsResolverTests {
@Test
void forFactoryMethodWhenNameIsEmptyThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> AutowiredInstantiationArgumentsResolver
.isThrownBy(() -> BeanInstanceSupplier
.forFactoryMethod(SingleArgFactory.class, ""))
.withMessage("'methodName' must not be empty");
}
@@ -119,7 +130,7 @@ class AutowiredInstantiationArgumentsResolverTests {
void forFactoryMethodWhenParameterTypesIsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(
() -> AutowiredInstantiationArgumentsResolver.forFactoryMethod(
() -> BeanInstanceSupplier.forFactoryMethod(
SingleArgFactory.class, "single", (Class<?>[]) null))
.withMessage("'parameterTypes' must not be null");
}
@@ -128,61 +139,136 @@ class AutowiredInstantiationArgumentsResolverTests {
void forFactoryMethodWhenParameterTypesContainsNullThrowsException() {
assertThatIllegalArgumentException()
.isThrownBy(
() -> AutowiredInstantiationArgumentsResolver.forFactoryMethod(
() -> BeanInstanceSupplier.forFactoryMethod(
SingleArgFactory.class, "single", String.class, null))
.withMessage("'parameterTypes' must not contain null elements");
}
@Test
void forFactoryMethodWhenNotFoundThrowsException() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forFactoryMethod(SingleArgFactory.class, "single", InputStream.class);
Source source = new Source(String.class, resolver);
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.resolve(registerBean)).withMessage(
.isThrownBy(() -> resolver.get(registerBean)).withMessage(
"Factory method 'single' with parameter types [java.io.InputStream] declared on class "
+ SingleArgFactory.class.getName() + " cannot be found");
}
@Test
void resolveWithActionWhenActionIsNullThrowsException() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
.forConstructor();
Source source = new Source(NoArgConstructor.class, resolver);
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.resolve(registerBean, null))
.withMessage("'action' must not be null");
void forFactoryMethodReturnsFactoryMethod() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forFactoryMethod(SingleArgFactory.class, "single", String.class);
Method factoryMethod = ReflectionUtils.findMethod(SingleArgFactory.class, "single", String.class);
assertThat(factoryMethod).isNotNull();
assertThat(resolver.getFactoryMethod()).isEqualTo(factoryMethod);
}
@Test
void resolveWithActionCallsAction() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
void withGeneratorWhenBiFunctionIsNullThrowsException() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor();
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.withGenerator(
(ThrowingBiFunction<RegisteredBean, AutowiredArguments, Object>) null))
.withMessage("'generator' must not be null");
}
@Test
void withGeneratorWhenFunctionIsNullThrowsException() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor();
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.withGenerator(
(ThrowingFunction<RegisteredBean, Object>) null))
.withMessage("'generator' must not be null");
}
@Test
void withGeneratorWhenSupplierIsNullThrowsException() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor();
assertThatIllegalArgumentException()
.isThrownBy(() -> resolver.withGenerator(
(ThrowingSupplier<Object>) null))
.withMessage("'generator' must not be null");
}
@Test
void getWithConstructorDoesNotSetResolvedFactoryMethod() throws Exception {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class);
Source source = new Source(SingleArgConstructor.class, resolver);
this.beanFactory.registerSingleton("one", "1");
Source source = new Source(SingleArgConstructor.class, resolver);
RegisteredBean registerBean = source.registerBean(this.beanFactory);
assertThat(registerBean.getMergedBeanDefinition().getResolvedFactoryMethod()).isNull();
source.getResolver().get(registerBean);
assertThat(registerBean.getMergedBeanDefinition().getResolvedFactoryMethod()).isNull();
}
@Test
void getWithFactoryMethodSetsResolvedFactoryMethod() {
Method factoryMethod = ReflectionUtils.findMethod(SingleArgFactory.class, "single", String.class);
assertThat(factoryMethod).isNotNull();
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forFactoryMethod(SingleArgFactory.class, "single", String.class);
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
assertThat(beanDefinition.getResolvedFactoryMethod()).isNull();
beanDefinition.setInstanceSupplier(resolver);
assertThat(beanDefinition.getResolvedFactoryMethod()).isEqualTo(factoryMethod);
}
@Test
void getWithGeneratorCallsBiFunction() throws Exception {
BeanRegistrar registrar = new BeanRegistrar(SingleArgConstructor.class);
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registerBean = registrar.registerBean(this.beanFactory);
List<Object> result = new ArrayList<>();
resolver.resolve(registerBean, result::add);
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class)
.withGenerator((registeredBean, args) -> result.add(args));
resolver.get(registerBean);
assertThat(result).hasSize(1);
assertThat(((AutowiredArguments) result.get(0)).toArray()).containsExactly("1");
}
@Test
void resolveWhenRegisteredBeanIsNullThrowsException() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
void getWithGeneratorCallsFunction() throws Exception {
BeanRegistrar registrar = new BeanRegistrar(SingleArgConstructor.class);
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registerBean = registrar.registerBean(this.beanFactory);
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class)
.withGenerator(registeredBean -> "1");
assertThat(resolver.get(registerBean)).isInstanceOf(String.class).isEqualTo("1");
}
@Test
void getWithGeneratorCallsSupplier() throws Exception {
BeanRegistrar registrar = new BeanRegistrar(SingleArgConstructor.class);
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registerBean = registrar.registerBean(this.beanFactory);
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class)
.withGenerator(() -> "1");
assertThat(resolver.get(registerBean)).isInstanceOf(String.class).isEqualTo("1");
}
@Test
void getWhenRegisteredBeanIsNullThrowsException() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class);
assertThatIllegalArgumentException().isThrownBy(() -> resolver.resolve(null))
assertThatIllegalArgumentException().isThrownBy(() -> resolver.get((RegisteredBean) null))
.withMessage("'registeredBean' must not be null");
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveAndInstantiate(Source source) {
void getWithNoGeneratorUsesReflection(Source source) throws Exception {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("testFactory", new SingleArgFactory());
RegisteredBean registerBean = source.registerBean(this.beanFactory);
Object instance = source.getResolver().resolveAndInstantiate(registerBean);
Object instance = source.getResolver().get(registerBean);
if (instance instanceof SingleArgConstructor singleArgConstructor) {
instance = singleArgConstructor.getString();
}
@@ -190,12 +276,12 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@ParameterizedResolverTest(Sources.INNER_CLASS_SINGLE_ARG)
void resolveAndInstantiateNested(Source source) {
void getNestedWithNoGeneratorUsesReflection(Source source) throws Exception {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("testFactory",
new Enclosing().new InnerSingleArgFactory());
RegisteredBean registerBean = source.registerBean(this.beanFactory);
Object instance = source.getResolver().resolveAndInstantiate(registerBean);
Object instance = source.getResolver().get(registerBean);
if (instance instanceof InnerSingleArgConstructor innerSingleArgConstructor) {
instance = innerSingleArgConstructor.getString();
}
@@ -203,38 +289,38 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@Test
void resolveNoArgConstructor() {
void resolveArgumentsWithNoArgConstructor() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(
NoArgConstructor.class);
this.beanFactory.registerBeanDefinition("test", beanDefinition);
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "test");
AutowiredArguments resolved = AutowiredInstantiationArgumentsResolver
.forConstructor().resolve(registeredBean);
AutowiredArguments resolved = BeanInstanceSupplier
.forConstructor().resolveArguments(registeredBean);
assertThat(resolved.toArray()).isEmpty();
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveSingleArgConstructor(Source source) {
void resolveArgumentsWithSingleArgConstructor(Source source) {
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registeredBean = source.registerBean(this.beanFactory);
assertThat(source.getResolver().resolve(registeredBean).toArray())
assertThat(source.getResolver().resolveArguments(registeredBean).toArray())
.containsExactly("1");
}
@ParameterizedResolverTest(Sources.INNER_CLASS_SINGLE_ARG)
void resolvedNestedSingleArgConstructor(Source source) {
void resolveArgumentsWithNestedSingleArgConstructor(Source source) {
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registeredBean = source.registerBean(this.beanFactory);
assertThat(source.getResolver().resolve(registeredBean).toArray())
assertThat(source.getResolver().resolveArguments(registeredBean).toArray())
.containsExactly("1");
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException(
void resolveArgumentsWithRequiredDependencyNotPresentThrowsUnsatisfiedDependencyException(
Source source) {
RegisteredBean registeredBean = source.registerBean(this.beanFactory);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(() -> source.getResolver().resolve(registeredBean))
.isThrownBy(() -> source.getResolver().resolveArguments(registeredBean))
.satisfies(ex -> {
assertThat(ex.getBeanName()).isEqualTo("testBean");
assertThat(ex.getInjectionPoint()).isNotNull();
@@ -244,16 +330,16 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@Test
void resolveInInstanceSupplierWithSelfReferenceThrowsException() {
void resolveArgumentsInInstanceSupplierWithSelfReferenceThrowsException() {
// SingleArgFactory.single(...) expects a String to be injected
// and our own bean is a String so it's a valid candidate
// and our own bean is a String, so it's a valid candidate
this.beanFactory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
beanDefinition.setInstanceSupplier(InstanceSupplier.of(registeredBean -> {
AutowiredArguments args = AutowiredInstantiationArgumentsResolver
AutowiredArguments args = BeanInstanceSupplier
.forFactoryMethod(SingleArgFactory.class, "single", String.class)
.resolve(registeredBean);
return new SingleArgFactory().single((String) args.get(0));
.resolveArguments(registeredBean);
return new SingleArgFactory().single(args.get(0));
}));
this.beanFactory.registerBeanDefinition("test", beanDefinition);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
@@ -261,83 +347,83 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@ParameterizedResolverTest(Sources.ARRAY_OF_BEANS)
void resolveArrayOfBeans(Source source) {
void resolveArgumentsWithArrayOfBeans(Source source) {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("two", "2");
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Object[]) arguments.get(0)).containsExactly("1", "2");
}
@ParameterizedResolverTest(Sources.ARRAY_OF_BEANS)
void resolveRequiredArrayOfBeansInjectEmptyArray(Source source) {
void resolveArgumentsWithRequiredArrayOfBeansInjectEmptyArray(Source source) {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Object[]) arguments.get(0)).isEmpty();
}
@ParameterizedResolverTest(Sources.LIST_OF_BEANS)
void resolveListOfBeans(Source source) {
void resolveArgumentsWithListOfBeans(Source source) {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("two", "2");
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat(arguments.getObject(0)).isInstanceOf(List.class).asList()
.containsExactly("1", "2");
}
@ParameterizedResolverTest(Sources.LIST_OF_BEANS)
void resolveRequiredListOfBeansInjectEmptyList(Source source) {
void resolveArgumentsWithRequiredListOfBeansInjectEmptyList(Source source) {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((List<?>) arguments.get(0)).isEmpty();
}
@ParameterizedResolverTest(Sources.SET_OF_BEANS)
@SuppressWarnings("unchecked")
void resolveSetOfBeans(Source source) {
void resolveArgumentsWithSetOfBeans(Source source) {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("two", "2");
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Set<String>) arguments.get(0)).containsExactly("1", "2");
}
@ParameterizedResolverTest(Sources.SET_OF_BEANS)
void resolveRequiredSetOfBeansInjectEmptySet(Source source) {
void resolveArgumentsWithRequiredSetOfBeansInjectEmptySet(Source source) {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Set<?>) arguments.get(0)).isEmpty();
}
@ParameterizedResolverTest(Sources.MAP_OF_BEANS)
@SuppressWarnings("unchecked")
void resolveMapOfBeans(Source source) {
void resolveArgumentsWithMapOfBeans(Source source) {
this.beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("two", "2");
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Map<String, String>) arguments.get(0))
.containsExactly(entry("one", "1"), entry("two", "2"));
}
@ParameterizedResolverTest(Sources.MAP_OF_BEANS)
void resolveRequiredMapOfBeansInjectEmptySet(Source source) {
void resolveArgumentsWithRequiredMapOfBeansInjectEmptySet(Source source) {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat((Map<?, ?>) arguments.get(0)).isEmpty();
}
@ParameterizedResolverTest(Sources.MULTI_ARGS)
void resolveMultiArgsConstructor(Source source) {
void resolveArgumentsWithMultiArgsConstructor(Source source) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Environment environment = mock(Environment.class);
this.beanFactory.registerResolvableDependency(ResourceLoader.class,
@@ -345,7 +431,7 @@ class AutowiredInstantiationArgumentsResolverTests {
this.beanFactory.registerSingleton("environment", environment);
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registerBean = source.registerBean(this.beanFactory);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(3);
assertThat(arguments.getObject(0)).isEqualTo(resourceLoader);
assertThat(arguments.getObject(1)).isEqualTo(environment);
@@ -354,7 +440,7 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@ParameterizedResolverTest(Sources.MIXED_ARGS)
void resolveMixedArgsConstructorWithUserValue(Source source) {
void resolveArgumentsWithMixedArgsConstructorWithUserValue(Source source) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Environment environment = mock(Environment.class);
this.beanFactory.registerResolvableDependency(ResourceLoader.class,
@@ -367,7 +453,7 @@ class AutowiredInstantiationArgumentsResolverTests {
beanDefinition.getConstructorArgumentValues()
.addIndexedArgumentValue(1, "user-value");
});
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(3);
assertThat(arguments.getObject(0)).isEqualTo(resourceLoader);
assertThat(arguments.getObject(1)).isEqualTo("user-value");
@@ -375,7 +461,7 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@ParameterizedResolverTest(Sources.MIXED_ARGS)
void resolveMixedArgsConstructorWithUserBeanReference(Source source) {
void resolveArgumentsWithMixedArgsConstructorWithUserBeanReference(Source source) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Environment environment = mock(Environment.class);
this.beanFactory.registerResolvableDependency(ResourceLoader.class,
@@ -390,7 +476,7 @@ class AutowiredInstantiationArgumentsResolverTests {
beanDefinition.getConstructorArgumentValues()
.addIndexedArgumentValue(1, new RuntimeBeanReference("two"));
});
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(3);
assertThat(arguments.getObject(0)).isEqualTo(resourceLoader);
assertThat(arguments.getObject(1)).isEqualTo("2");
@@ -398,9 +484,9 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@Test
void resolveUserValueWithTypeConversionRequired() {
void resolveArgumentsWithUserValueWithTypeConversionRequired() {
Source source = new Source(CharDependency.class,
AutowiredInstantiationArgumentsResolver.forConstructor(char.class));
BeanInstanceSupplier.forConstructor(char.class));
RegisteredBean registerBean = source.registerBean(this.beanFactory,
beanDefinition -> {
beanDefinition
@@ -408,37 +494,37 @@ class AutowiredInstantiationArgumentsResolverTests {
beanDefinition.getConstructorArgumentValues()
.addIndexedArgumentValue(0, "\\");
});
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat(arguments.getObject(0)).isInstanceOf(Character.class).isEqualTo('\\');
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveUserValueWithBeanReference(Source source) {
void resolveArgumentsWithUserValueWithBeanReference(Source source) {
this.beanFactory.registerSingleton("stringBean", "string");
RegisteredBean registerBean = source.registerBean(this.beanFactory,
beanDefinition -> beanDefinition.getConstructorArgumentValues()
.addIndexedArgumentValue(0,
new RuntimeBeanReference("stringBean")));
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat(arguments.getObject(0)).isEqualTo("string");
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveUserValueWithBeanDefinition(Source source) {
void resolveArgumentsWithUserValueWithBeanDefinition(Source source) {
AbstractBeanDefinition userValue = BeanDefinitionBuilder
.rootBeanDefinition(String.class, () -> "string").getBeanDefinition();
RegisteredBean registerBean = source.registerBean(this.beanFactory,
beanDefinition -> beanDefinition.getConstructorArgumentValues()
.addIndexedArgumentValue(0, userValue));
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat(arguments.getObject(0)).isEqualTo("string");
}
@ParameterizedResolverTest(Sources.SINGLE_ARG)
void resolveUserValueThatIsAlreadyResolved(Source source) {
void resolveArgumentsWithUserValueThatIsAlreadyResolved(Source source) {
RegisteredBean registerBean = source.registerBean(this.beanFactory);
BeanDefinition mergedBeanDefinition = this.beanFactory
.getMergedBeanDefinition("testBean");
@@ -446,13 +532,13 @@ class AutowiredInstantiationArgumentsResolverTests {
valueHolder.setConvertedValue("this is an a");
mergedBeanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
valueHolder);
AutowiredArguments arguments = source.getResolver().resolve(registerBean);
AutowiredArguments arguments = source.getResolver().resolveArguments(registerBean);
assertThat(arguments.toArray()).hasSize(1);
assertThat(arguments.getObject(0)).isEqualTo("this is an a");
}
@Test
void resolveWhenUsingShortcutsInjectsDirectly() {
void resolveArgumentsWhenUsingShortcutsInjectsDirectly() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory() {
@Override
@@ -462,35 +548,35 @@ class AutowiredInstantiationArgumentsResolverTests {
}
};
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class);
Source source = new Source(String.class, resolver);
beanFactory.registerSingleton("one", "1");
RegisteredBean registeredBean = source.registerBean(beanFactory);
assertThatExceptionOfType(AssertionError.class)
.isThrownBy(() -> resolver.resolve(registeredBean));
assertThat(resolver.withShortcuts("one").resolve(registeredBean).toArray())
.isThrownBy(() -> resolver.resolveArguments(registeredBean));
assertThat(resolver.withShortcuts("one").resolveArguments(registeredBean).toArray())
.containsExactly("1");
}
@Test
void resolveRegistersDependantBeans() {
AutowiredInstantiationArgumentsResolver resolver = AutowiredInstantiationArgumentsResolver
void resolveArgumentsRegistersDependantBeans() {
BeanInstanceSupplier resolver = BeanInstanceSupplier
.forConstructor(String.class);
Source source = new Source(SingleArgConstructor.class, resolver);
beanFactory.registerSingleton("one", "1");
this.beanFactory.registerSingleton("one", "1");
RegisteredBean registeredBean = source.registerBean(this.beanFactory);
resolver.resolve(registeredBean);
resolver.resolveArguments(registeredBean);
assertThat(this.beanFactory.getDependentBeans("one")).containsExactly("testBean");
}
/**
* Parameterized {@link Using} test backed by a {@link Sources}.
* Parameterized test backed by a {@link Sources}.
*/
@Retention(RetentionPolicy.RUNTIME)
@ParameterizedTest
@ArgumentsSource(SourcesArguments.class)
static @interface ParameterizedResolverTest {
@interface ParameterizedResolverTest {
Sources value();
@@ -510,8 +596,7 @@ class AutowiredInstantiationArgumentsResolverTests {
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context)
throws Exception {
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return this.source.provideArguments(context);
}
@@ -523,27 +608,25 @@ class AutowiredInstantiationArgumentsResolverTests {
enum Sources {
SINGLE_ARG {
@Override
protected void setup() {
add(SingleArgConstructor.class, AutowiredInstantiationArgumentsResolver
add(SingleArgConstructor.class, BeanInstanceSupplier
.forConstructor(String.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
SingleArgFactory.class, "single", String.class));
}
},
INNER_CLASS_SINGLE_ARG {
@Override
protected void setup() {
add(Enclosing.InnerSingleArgConstructor.class,
AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier
.forConstructor(String.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
Enclosing.InnerSingleArgFactory.class, "single",
String.class));
}
@@ -551,71 +634,66 @@ class AutowiredInstantiationArgumentsResolverTests {
},
ARRAY_OF_BEANS {
@Override
protected void setup() {
add(BeansCollectionConstructor.class,
AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier
.forConstructor(String[].class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
BeansCollectionFactory.class, "array", String[].class));
}
},
LIST_OF_BEANS {
@Override
protected void setup() {
add(BeansCollectionConstructor.class,
AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier
.forConstructor(List.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
BeansCollectionFactory.class, "list", List.class));
}
},
SET_OF_BEANS {
@Override
protected void setup() {
add(BeansCollectionConstructor.class,
AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier
.forConstructor(Set.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
BeansCollectionFactory.class, "set", Set.class));
}
},
MAP_OF_BEANS {
@Override
protected void setup() {
add(BeansCollectionConstructor.class,
AutowiredInstantiationArgumentsResolver
BeanInstanceSupplier
.forConstructor(Map.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
BeansCollectionFactory.class, "map", Map.class));
}
},
MULTI_ARGS {
@Override
protected void setup() {
add(MultiArgsConstructor.class,
AutowiredInstantiationArgumentsResolver.forConstructor(
BeanInstanceSupplier.forConstructor(
ResourceLoader.class, Environment.class,
ObjectProvider.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
MultiArgsFactory.class, "multiArgs", ResourceLoader.class,
Environment.class, ObjectProvider.class));
}
@@ -623,14 +701,13 @@ class AutowiredInstantiationArgumentsResolverTests {
},
MIXED_ARGS {
@Override
protected void setup() {
add(MixedArgsConstructor.class,
AutowiredInstantiationArgumentsResolver.forConstructor(
BeanInstanceSupplier.forConstructor(
ResourceLoader.class, String.class, Environment.class));
add(String.class,
AutowiredInstantiationArgumentsResolver.forFactoryMethod(
BeanInstanceSupplier.forFactoryMethod(
MixedArgsFactory.class, "mixedArgs", ResourceLoader.class,
String.class, Environment.class));
}
@@ -639,7 +716,7 @@ class AutowiredInstantiationArgumentsResolverTests {
private final List<Arguments> arguments;
private Sources() {
Sources() {
this.arguments = new ArrayList<>();
setup();
}
@@ -647,7 +724,7 @@ class AutowiredInstantiationArgumentsResolverTests {
protected abstract void setup();
protected final void add(Class<?> beanClass,
AutowiredInstantiationArgumentsResolver resolver) {
BeanInstanceSupplier resolver) {
this.arguments.add(Arguments.of(new Source(beanClass, resolver)));
}
@@ -657,16 +734,12 @@ class AutowiredInstantiationArgumentsResolverTests {
}
static class Source {
static class BeanRegistrar {
private final Class<?> beanClass;
final Class<?> beanClass;
private final AutowiredInstantiationArgumentsResolver resolver;
public Source(Class<?> beanClass,
AutowiredInstantiationArgumentsResolver resolver) {
public BeanRegistrar(Class<?> beanClass) {
this.beanClass = beanClass;
this.resolver = resolver;
}
RegisteredBean registerBean(DefaultListableBeanFactory beanFactory) {
@@ -685,8 +758,19 @@ class AutowiredInstantiationArgumentsResolverTests {
beanFactory.registerBeanDefinition(beanName, beanDefinition);
return RegisteredBean.of(beanFactory, beanName);
}
}
AutowiredInstantiationArgumentsResolver getResolver() {
static class Source extends BeanRegistrar {
private final BeanInstanceSupplier resolver;
public Source(Class<?> beanClass,
BeanInstanceSupplier resolver) {
super(beanClass);
this.resolver = resolver;
}
BeanInstanceSupplier getResolver() {
return this.resolver;
}
@@ -715,7 +799,7 @@ class AutowiredInstantiationArgumentsResolverTests {
}
String getString() {
return string;
return this.string;
}
}
@@ -834,7 +918,7 @@ class AutowiredInstantiationArgumentsResolverTests {
}
static interface MethodOnInterface {
interface MethodOnInterface {
default String test() {
return "Test";

View File

@@ -172,7 +172,7 @@ class InstanceSupplierCodeGeneratorTests {
instanceSupplier);
assertThat(bean).isInstanceOf(TestBeanWithPrivateConstructor.class);
assertThat(compiled.getSourceFile())
.contains("resolveAndInstantiate(registeredBean)");
.contains("return BeanInstanceSupplier.forConstructor();");
});
assertThat(getReflectionHints().getTypeHint(TestBeanWithPrivateConstructor.class))
.satisfies(hasConstructorWithMode(ExecutableMode.INVOKE));
@@ -211,7 +211,8 @@ class InstanceSupplierCodeGeneratorTests {
assertThat(bean).isInstanceOf(String.class);
assertThat(bean).isEqualTo("Hello");
assertThat(compiled.getSourceFile())
.contains("resolveAndInstantiate(registeredBean)");
.contains("BeanInstanceSupplier.forFactoryMethod")
.doesNotContain("withGenerator");
});
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
.satisfies(hasMethodWithMode(ExecutableMode.INVOKE));
@@ -271,7 +272,7 @@ class InstanceSupplierCodeGeneratorTests {
Integer bean = getBean(beanFactory, beanDefinition, instanceSupplier);
assertThat(bean).isInstanceOf(Integer.class);
assertThat(bean).isEqualTo(42);
assertThat(compiled.getSourceFile()).contains(") throws Exception {");
assertThat(compiled.getSourceFile()).doesNotContain(") throws Exception {");
});
assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class))
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));

View File

@@ -0,0 +1,60 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link RootBeanDefinition}.
*
* @author Stephane Nicoll
*/
class RootBeanDefinitionTests {
@Test
void setInstanceSetResolvedFactoryMethod() {
InstanceSupplier<?> instanceSupplier = mock(InstanceSupplier.class);
Method method = ReflectionUtils.findMethod(String.class, "toString");
given(instanceSupplier.getFactoryMethod()).willReturn(method);
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
beanDefinition.setInstanceSupplier(instanceSupplier);
assertThat(beanDefinition.getResolvedFactoryMethod()).isEqualTo(method);
verify(instanceSupplier).getFactoryMethod();
}
@Test
void setInstanceDoesNotOverrideResolvedFactoryMethodWithNull() {
InstanceSupplier<?> instanceSupplier = mock(InstanceSupplier.class);
given(instanceSupplier.getFactoryMethod()).willReturn(null);
Method method = ReflectionUtils.findMethod(String.class, "toString");
RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class);
beanDefinition.setResolvedFactoryMethod(method);
beanDefinition.setInstanceSupplier(instanceSupplier);
assertThat(beanDefinition.getResolvedFactoryMethod()).isEqualTo(method);
verify(instanceSupplier).getFactoryMethod();
}
}