From 95710646d1b54d32eb70d3fbc1e8dc7ce95c3584 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 8 Mar 2023 16:49:25 +0100 Subject: [PATCH] Pass pre-determined merged bean definition into InstanceSupplier (for inner beans) Replaces useless protected obtainFromSupplier method with obtainInstanceFromSupplier. Moves InstanceSupplier handling to appropriate subclass (DefaultListableBeanFactory). BeanInstanceSupplier throws BeanInstantiationException instead of BeanCreationException. Closes gh-29803 --- .../factory/aot/BeanInstanceSupplier.java | 95 ++++++++----------- .../AbstractAutowireCapableBeanFactory.java | 56 ++++++----- .../support/DefaultListableBeanFactory.java | 14 ++- .../factory/support/InstanceSupplier.java | 16 ++-- .../beans/factory/support/RegisteredBean.java | 37 ++++---- .../support/BeanFactorySupplierTests.java | 50 ++++++++-- 6 files changed, 151 insertions(+), 117 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java index ab98f89439a..a160d986f89 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java @@ -27,9 +27,9 @@ import java.util.Set; import java.util.stream.Collectors; import org.springframework.aot.hint.ExecutableMode; +import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; -import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -97,11 +97,13 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl private BeanInstanceSupplier(ExecutableLookup lookup, @Nullable ThrowingBiFunction generator, @Nullable String[] shortcuts) { + this.lookup = lookup; this.generator = generator; this.shortcuts = shortcuts; } + /** * Create a {@link BeanInstanceSupplier} that resolves * arguments for the specified bean constructor. @@ -109,9 +111,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl * @param parameterTypes the constructor parameter types * @return a new {@link BeanInstanceSupplier} instance */ - public static BeanInstanceSupplier forConstructor( - Class... parameterTypes) { - + public static BeanInstanceSupplier forConstructor(Class... parameterTypes) { Assert.notNull(parameterTypes, "'parameterTypes' must not be null"); Assert.noNullElements(parameterTypes, "'parameterTypes' must not contain null elements"); return new BeanInstanceSupplier<>(new ConstructorLookup(parameterTypes), null, null); @@ -149,11 +149,11 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl * @param generator a {@link ThrowingBiFunction} that uses the * {@link RegisteredBean} and resolved {@link AutowiredArguments} to * instantiate the underlying bean - * @return a new {@link BeanInstanceSupplier} instance with the specified - * generator + * @return a new {@link BeanInstanceSupplier} instance with the specified generator */ public BeanInstanceSupplier withGenerator( ThrowingBiFunction generator) { + Assert.notNull(generator, "'generator' must not be null"); return new BeanInstanceSupplier<>(this.lookup, generator, this.shortcuts); } @@ -163,11 +163,9 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl * {@code generator} function to instantiate the underlying bean. * @param generator a {@link ThrowingFunction} that uses the * {@link RegisteredBean} to instantiate the underlying bean - * @return a new {@link BeanInstanceSupplier} instance with the specified - * generator + * @return a new {@link BeanInstanceSupplier} instance with the specified generator */ - public BeanInstanceSupplier withGenerator( - ThrowingFunction generator) { + public BeanInstanceSupplier withGenerator(ThrowingFunction generator) { Assert.notNull(generator, "'generator' must not be null"); return new BeanInstanceSupplier<>(this.lookup, (registeredBean, args) -> generator.apply(registeredBean), this.shortcuts); @@ -176,10 +174,8 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl /** * Return a new {@link BeanInstanceSupplier} instance that uses the specified * {@code generator} supplier to instantiate the underlying bean. - * @param generator a {@link ThrowingSupplier} to instantiate the underlying - * bean - * @return a new {@link BeanInstanceSupplier} instance with the specified - * generator + * @param generator a {@link ThrowingSupplier} to instantiate the underlying bean + * @return a new {@link BeanInstanceSupplier} instance with the specified generator */ public BeanInstanceSupplier withGenerator(ThrowingSupplier generator) { Assert.notNull(generator, "'generator' must not be null"); @@ -282,8 +278,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl if (executable instanceof Method method) { return new MethodParameter(method, index); } - throw new IllegalStateException( - "Unsupported executable " + executable.getClass().getName()); + throw new IllegalStateException("Unsupported executable: " + executable.getClass().getName()); } private ConstructorArgumentValues resolveArgumentValues( @@ -303,9 +298,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl return resolved; } - private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver, - ValueHolder valueHolder) { - + private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver, ValueHolder valueHolder) { if (valueHolder.isConverted()) { return valueHolder; } @@ -331,8 +324,7 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl } try { try { - return beanFactory.resolveDependency(dependencyDescriptor, beanName, - autowiredBeans, typeConverter); + return beanFactory.resolveDependency(dependencyDescriptor, beanName, autowiredBeans, typeConverter); } catch (NoSuchBeanDefinitionException ex) { if (parameterType.isArray()) { @@ -348,47 +340,45 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl } } catch (BeansException ex) { - throw new UnsatisfiedDependencyException(null, beanName, - new InjectionPoint(parameter), ex); + throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex); } } @SuppressWarnings("unchecked") - private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable, - Object[] arguments) { - - try { - if (executable instanceof Constructor constructor) { - return (T) instantiate(constructor, arguments); + private T instantiate(ConfigurableBeanFactory beanFactory, Executable executable, Object[] args) { + if (executable instanceof Constructor constructor) { + try { + return (T) instantiate(constructor, args); } - if (executable instanceof Method method) { - return (T) instantiate(beanFactory, method, arguments); + catch (Exception ex) { + throw new BeanInstantiationException(constructor, ex.getMessage(), ex); } } - catch (Exception ex) { - throw new BeanCreationException( - "Unable to instantiate bean using " + executable, ex); + if (executable instanceof Method method) { + try { + return (T) instantiate(beanFactory, method, args); + } + catch (Exception ex) { + throw new BeanInstantiationException(method, ex.getMessage(), ex); + } } - throw new IllegalStateException( - "Unsupported executable " + executable.getClass().getName()); + throw new IllegalStateException("Unsupported executable " + executable.getClass().getName()); } - private Object instantiate(Constructor constructor, Object[] arguments) throws Exception { + private Object instantiate(Constructor constructor, Object[] args) throws Exception { Class declaringClass = constructor.getDeclaringClass(); if (ClassUtils.isInnerClass(declaringClass)) { Object enclosingInstance = createInstance(declaringClass.getEnclosingClass()); - arguments = ObjectUtils.addObjectToArray(arguments, enclosingInstance, 0); + args = ObjectUtils.addObjectToArray(args, enclosingInstance, 0); } ReflectionUtils.makeAccessible(constructor); - return constructor.newInstance(arguments); + return constructor.newInstance(args); } - private Object instantiate(ConfigurableBeanFactory beanFactory, Method method, - Object[] arguments) { - - ReflectionUtils.makeAccessible(method); + private Object instantiate(ConfigurableBeanFactory beanFactory, Method method, Object[] args) throws Exception { Object target = getFactoryMethodTarget(beanFactory, method); - return ReflectionUtils.invokeMethod(method, target, arguments); + ReflectionUtils.makeAccessible(method); + return method.invoke(target, args); } @Nullable @@ -416,13 +406,13 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl return Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors.joining(", ")); } + /** * Performs lookup of the {@link Executable}. */ static abstract class ExecutableLookup { abstract Executable get(RegisteredBean registeredBean); - } @@ -433,12 +423,10 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl private final Class[] parameterTypes; - ConstructorLookup(Class[] parameterTypes) { this.parameterTypes = parameterTypes; } - @Override public Executable get(RegisteredBean registeredBean) { Class beanClass = registeredBean.getBeanClass(); @@ -456,10 +444,8 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl @Override public String toString() { - return "Constructor with parameter types [%s]".formatted( - toCommaSeparatedNames(this.parameterTypes)); + return "Constructor with parameter types [%s]".formatted(toCommaSeparatedNames(this.parameterTypes)); } - } @@ -474,23 +460,19 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl private final Class[] parameterTypes; - - FactoryMethodLookup(Class declaringClass, String methodName, - Class[] parameterTypes) { + FactoryMethodLookup(Class declaringClass, String methodName, Class[] parameterTypes) { this.declaringClass = declaringClass; this.methodName = methodName; this.parameterTypes = parameterTypes; } - @Override public Executable get(RegisteredBean registeredBean) { return get(); } Method get() { - Method method = ReflectionUtils.findMethod(this.declaringClass, - this.methodName, this.parameterTypes); + Method method = ReflectionUtils.findMethod(this.declaringClass, this.methodName, this.parameterTypes); Assert.notNull(method, () -> "%s cannot be found".formatted(this)); return method; } @@ -501,7 +483,6 @@ public final class BeanInstanceSupplier extends AutowiredElementResolver impl this.methodName, toCommaSeparatedNames(this.parameterTypes), this.declaringClass); } - } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 7c17284a442..abf24370f45 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -61,7 +61,6 @@ import org.springframework.beans.factory.config.AutowiredPropertyMarker; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; @@ -1154,7 +1153,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac Supplier instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { - return obtainFromSupplier(instanceSupplier, beanName); + return obtainFromSupplier(instanceSupplier, beanName, mbd); } if (mbd.getFactoryMethodName() != null) { @@ -1203,38 +1202,20 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac * @param supplier the configured supplier * @param beanName the corresponding bean name * @return a BeanWrapper for the new instance - * @since 5.0 - * @see #getObjectForBeanInstance */ - protected BeanWrapper obtainFromSupplier(Supplier supplier, String beanName) { - Object instance = obtainInstanceFromSupplier(supplier, beanName); - if (instance == null) { - instance = new NullBean(); - } - BeanWrapper bw = new BeanWrapperImpl(instance); - initBeanWrapper(bw); - return bw; - } - - @Nullable - private Object obtainInstanceFromSupplier(Supplier supplier, String beanName) { + private BeanWrapper obtainFromSupplier(Supplier supplier, String beanName, RootBeanDefinition mbd) { String outerBean = this.currentlyCreatedBean.get(); this.currentlyCreatedBean.set(beanName); + Object instance; + try { - if (supplier instanceof InstanceSupplier instanceSupplier) { - return instanceSupplier.get(RegisteredBean.of((ConfigurableListableBeanFactory) this, beanName)); - } - if (supplier instanceof ThrowingSupplier throwingSupplier) { - return throwingSupplier.getWithException(); - } - return supplier.get(); + instance = obtainInstanceFromSupplier(supplier, beanName, mbd); } catch (Throwable ex) { if (ex instanceof BeansException beansException) { throw beansException; } - throw new BeanCreationException(beanName, - "Instantiation of supplied bean failed", ex); + throw new BeanCreationException(beanName, "Instantiation of supplied bean failed", ex); } finally { if (outerBean != null) { @@ -1244,6 +1225,31 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac this.currentlyCreatedBean.remove(); } } + + if (instance == null) { + instance = new NullBean(); + } + BeanWrapper bw = new BeanWrapperImpl(instance); + initBeanWrapper(bw); + return bw; + } + + /** + * Obtain a bean instance from the given supplier. + * @param supplier the configured supplier + * @param beanName the corresponding bean name + * @param mbd the bean definition for the bean + * @return the bean instance (possibly {@code null}) + * @since 6.0.7 + */ + @Nullable + protected Object obtainInstanceFromSupplier(Supplier supplier, String beanName, RootBeanDefinition mbd) + throws Exception { + + if (supplier instanceof ThrowingSupplier throwingSupplier) { + return throwingSupplier.getWithException(); + } + return supplier.get(); } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 5c8cea79336..14cad2960be 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -40,6 +40,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Stream; import jakarta.inject.Provider; @@ -937,6 +938,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return (this.configurationFrozen || super.isBeanEligibleForMetadataCaching(beanName)); } + @Override + @Nullable + protected Object obtainInstanceFromSupplier(Supplier supplier, String beanName, RootBeanDefinition mbd) + throws Exception { + + if (supplier instanceof InstanceSupplier instanceSupplier) { + return instanceSupplier.get(RegisteredBean.of(this, beanName, mbd)); + } + return super.obtainInstanceFromSupplier(supplier, beanName, mbd); + } + @Override public void preInstantiateSingletons() throws BeansException { if (logger.isTraceEnabled()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/InstanceSupplier.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/InstanceSupplier.java index eddbc795472..8d930b35739 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/InstanceSupplier.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/InstanceSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -35,6 +35,7 @@ import org.springframework.util.function.ThrowingSupplier; * @since 6.0 * @param the type of instance supplied by this supplier * @see RegisteredBean + * @see org.springframework.beans.factory.aot.BeanInstanceSupplier */ @FunctionalInterface public interface InstanceSupplier extends ThrowingSupplier { @@ -74,19 +75,17 @@ public interface InstanceSupplier extends ThrowingSupplier { */ default InstanceSupplier andThen( ThrowingBiFunction after) { + Assert.notNull(after, "'after' function must not be null"); return new InstanceSupplier<>() { - @Override public V get(RegisteredBean registeredBean) throws Exception { return after.applyWithException(registeredBean, InstanceSupplier.this.get(registeredBean)); } - @Override public Method getFactoryMethod() { return InstanceSupplier.this.getFactoryMethod(); } - }; } @@ -115,22 +114,21 @@ public interface InstanceSupplier extends ThrowingSupplier { */ static InstanceSupplier using(@Nullable Method factoryMethod, ThrowingSupplier supplier) { Assert.notNull(supplier, "Supplier must not be null"); - if (supplier instanceof InstanceSupplier instanceSupplier - && instanceSupplier.getFactoryMethod() == factoryMethod) { + + if (supplier instanceof InstanceSupplier instanceSupplier && + instanceSupplier.getFactoryMethod() == factoryMethod) { return instanceSupplier; } - return new InstanceSupplier<>() { + return new InstanceSupplier<>() { @Override public T get(RegisteredBean registeredBean) throws Exception { return supplier.getWithException(); } - @Override public Method getFactoryMethod() { return factoryMethod; } - }; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java index 06a17bd563c..c8c5487153c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -80,6 +80,18 @@ public final class RegisteredBean { null); } + /** + * Create a new {@link RegisteredBean} instance for a regular bean. + * @param beanFactory the source bean factory + * @param beanName the bean name + * @param mbd the pre-determined merged bean definition + * @return a new {@link RegisteredBean} instance + * @since 6.0.7 + */ + static RegisteredBean of(ConfigurableListableBeanFactory beanFactory, String beanName, RootBeanDefinition mbd) { + return new RegisteredBean(beanFactory, () -> beanName, false, () -> mbd, null); + } + /** * Create a new {@link RegisteredBean} instance for an inner-bean. * @param parent the parent of the inner-bean @@ -220,45 +232,34 @@ public final class RegisteredBean { @Nullable private volatile String resolvedBeanName; - - InnerBeanResolver(RegisteredBean parent, @Nullable String innerBeanName, - BeanDefinition innerBeanDefinition) { - - Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, - parent.getBeanFactory()); + InnerBeanResolver(RegisteredBean parent, @Nullable String innerBeanName, BeanDefinition innerBeanDefinition) { + Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, parent.getBeanFactory()); this.parent = parent; this.innerBeanName = innerBeanName; this.innerBeanDefinition = innerBeanDefinition; } - String resolveBeanName() { String resolvedBeanName = this.resolvedBeanName; if (resolvedBeanName != null) { return resolvedBeanName; } - resolvedBeanName = resolveInnerBean( - (beanName, mergedBeanDefinition) -> beanName); + resolvedBeanName = resolveInnerBean((beanName, mergedBeanDefinition) -> beanName); this.resolvedBeanName = resolvedBeanName; return resolvedBeanName; } RootBeanDefinition resolveMergedBeanDefinition() { - return resolveInnerBean( - (beanName, mergedBeanDefinition) -> mergedBeanDefinition); + return resolveInnerBean((beanName, mergedBeanDefinition) -> mergedBeanDefinition); } - private T resolveInnerBean( - BiFunction resolver) { - + private T resolveInnerBean(BiFunction resolver) { // Always use a fresh BeanDefinitionValueResolver in case the parent merged bean definition has changed. BeanDefinitionValueResolver beanDefinitionValueResolver = new BeanDefinitionValueResolver( (AbstractAutowireCapableBeanFactory) this.parent.getBeanFactory(), this.parent.getBeanName(), this.parent.getMergedBeanDefinition()); - return beanDefinitionValueResolver.resolveInnerBean(this.innerBeanName, - this.innerBeanDefinition, resolver); + return beanDefinitionValueResolver.resolveInnerBean(this.innerBeanName, this.innerBeanDefinition, resolver); } - } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactorySupplierTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactorySupplierTests.java index d07cd08abda..d147b24d350 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactorySupplierTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactorySupplierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 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. @@ -27,10 +27,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** - * Tests for {@link AbstractAutowireCapableBeanFactory} instance supplier - * support. + * Tests for {@link AbstractAutowireCapableBeanFactory} instance supplier support. * * @author Phillip Webb + * @author Juergen Hoeller */ public class BeanFactorySupplierTests { @@ -44,13 +44,38 @@ public class BeanFactorySupplierTests { } @Test - void getBeanWhenUsingInstanceSupplier() { + void getBeanWithInnerBeanUsingRegularSupplier() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); RootBeanDefinition beanDefinition = new RootBeanDefinition(); - beanDefinition.setInstanceSupplier(InstanceSupplier - .of(registeredBean -> "I am bean " + registeredBean.getBeanName())); + beanDefinition.setInstanceSupplier(() -> "I am supplied"); + RootBeanDefinition outerBean = new RootBeanDefinition(String.class); + outerBean.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition); + beanFactory.registerBeanDefinition("test", outerBean); + assertThat(beanFactory.getBean("test")).asString().startsWith("I am supplied"); + } + + @Test + void getBeanWhenUsingInstanceSupplier() { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class); + beanDefinition.setInstanceSupplier(InstanceSupplier.of(registeredBean -> + "I am bean " + registeredBean.getBeanName() + " of " + registeredBean.getBeanClass())); beanFactory.registerBeanDefinition("test", beanDefinition); - assertThat(beanFactory.getBean("test")).isEqualTo("I am bean test"); + assertThat(beanFactory.getBean("test")).isEqualTo("I am bean test of class java.lang.String"); + } + + @Test + void getBeanWithInnerBeanUsingInstanceSupplier() { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + RootBeanDefinition beanDefinition = new RootBeanDefinition(String.class); + beanDefinition.setInstanceSupplier(InstanceSupplier.of(registeredBean -> + "I am bean " + registeredBean.getBeanName() + " of " + registeredBean.getBeanClass())); + RootBeanDefinition outerBean = new RootBeanDefinition(String.class); + outerBean.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition); + beanFactory.registerBeanDefinition("test", outerBean); + assertThat(beanFactory.getBean("test")).asString() + .startsWith("I am bean (inner bean)") + .endsWith(" of class java.lang.String"); } @Test @@ -62,6 +87,17 @@ public class BeanFactorySupplierTests { assertThat(beanFactory.getBean("test")).isEqualTo("I am supplied"); } + @Test + void getBeanWithInnerBeanUsingThrowableSupplier() { + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + RootBeanDefinition beanDefinition = new RootBeanDefinition(); + beanDefinition.setInstanceSupplier(ThrowingSupplier.of(() -> "I am supplied")); + RootBeanDefinition outerBean = new RootBeanDefinition(String.class); + outerBean.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition); + beanFactory.registerBeanDefinition("test", outerBean); + assertThat(beanFactory.getBean("test")).asString().startsWith("I am supplied"); + } + @Test void getBeanWhenUsingThrowableSupplierThatThrowsCheckedException() { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();