Add InstanceSupplier and RegisteredBean support
Add a new `InstanceSupplier` interface that can be used as an alternative to a regular bean `Supplier` when details about the bean being supplied are required to instantiate it. The new interface accepts a `RegisteredBean` instance which provides access to the bean name, the bean factory creating the bean and the bean definition. This interface is primarily designed to allow AOT generated code to autowire dependencies into the instance. See gh-28414
This commit is contained in:
parent
d31eb4c0f1
commit
3209d7f126
|
|
@ -79,6 +79,7 @@ import org.springframework.util.ObjectUtils;
|
|||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
/**
|
||||
* Abstract bean factory superclass that implements default bean creation,
|
||||
|
|
@ -1199,19 +1200,40 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
|
||||
/**
|
||||
* Obtain a bean instance from the given supplier.
|
||||
* @param instanceSupplier the configured supplier
|
||||
* @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<?> instanceSupplier, String beanName) {
|
||||
Object instance;
|
||||
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;
|
||||
}
|
||||
|
||||
private Object obtainInstanceFromSupplier(Supplier<?> supplier, String beanName) {
|
||||
String outerBean = this.currentlyCreatedBean.get();
|
||||
this.currentlyCreatedBean.set(beanName);
|
||||
try {
|
||||
instance = instanceSupplier.get();
|
||||
if (supplier instanceof InstanceSupplier<?> instanceSupplier) {
|
||||
return instanceSupplier.get(RegisteredBean.of(this, beanName));
|
||||
}
|
||||
if (supplier instanceof ThrowingSupplier<?> throwableSupplier) {
|
||||
return throwableSupplier.getWithException();
|
||||
}
|
||||
return supplier.get();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (ex instanceof BeansException beansException) {
|
||||
throw beansException;
|
||||
}
|
||||
throw new BeanCreationException(beanName,
|
||||
"Instantiation of supplied bean failed", ex);
|
||||
}
|
||||
finally {
|
||||
if (outerBean != null) {
|
||||
|
|
@ -1221,13 +1243,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
this.currentlyCreatedBean.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (instance == null) {
|
||||
instance = new NullBean();
|
||||
}
|
||||
BeanWrapper bw = new BeanWrapperImpl(instance);
|
||||
initBeanWrapper(bw);
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.util.function.Supplier;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
/**
|
||||
* Specialized {@link Supplier} that can be set on a
|
||||
* {@link AbstractBeanDefinition#setInstanceSupplier(Supplier) BeanDefinition}
|
||||
* when details about the {@link RegisteredBean registered bean} are needed to
|
||||
* supply the instance.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
* @param <T> the type of instance supplied by this supplier
|
||||
* @see RegisteredBean
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface InstanceSupplier<T> extends ThrowingSupplier<T> {
|
||||
|
||||
@Override
|
||||
default T getWithException() {
|
||||
throw new IllegalStateException("No RegisteredBean parameter provided");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the supplied instance.
|
||||
* @param registeredBean the registered bean requesting the instance
|
||||
* @return the supplied instance
|
||||
* @throws Exception on error
|
||||
*/
|
||||
T get(RegisteredBean registeredBean) throws Exception;
|
||||
|
||||
/**
|
||||
* Return a composed instance supplier that first obtains the instance from
|
||||
* this supplier, and then applied the {@code after} function to obtain the
|
||||
* result.
|
||||
* @param <V> the type of output of the {@code after} function, and of the
|
||||
* composed function
|
||||
* @param after the function to apply after the instance is obtained
|
||||
* @return a composed instance supplier
|
||||
*/
|
||||
default <V> InstanceSupplier<V> andThen(
|
||||
ThrowingBiFunction<RegisteredBean, ? super T, ? extends V> after) {
|
||||
Assert.notNull(after, "After must not be null");
|
||||
return registeredBean -> after.applyWithException(registeredBean,
|
||||
get(registeredBean));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create an {@link InstanceSupplier} from a
|
||||
* {@link ThrowingSupplier}.
|
||||
* @param <T> the type of instance supplied by this supplier
|
||||
* @param supplier the source supplier
|
||||
* @return a new {@link InstanceSupplier}
|
||||
*/
|
||||
static <T> InstanceSupplier<T> using(ThrowingSupplier<T> supplier) {
|
||||
Assert.notNull(supplier, "Supplier must not be null");
|
||||
if (supplier instanceof InstanceSupplier<T> instanceSupplier) {
|
||||
return instanceSupplier;
|
||||
}
|
||||
return registeredBean -> supplier.getWithException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lambda friendly method that can be used to create a
|
||||
* {@link InstanceSupplier} and add post processors in a single call. For
|
||||
* example: {@code
|
||||
* InstanceSupplier.of(registeredBean -> ...).withPostProcessor(...)}.
|
||||
* @param <T> the type of instance supplied by this supplier
|
||||
* @param instanceSupplier the source instance supplier
|
||||
* @return a new {@link InstanceSupplier}
|
||||
*/
|
||||
static <T> InstanceSupplier<T> of(InstanceSupplier<T> instanceSupplier) {
|
||||
Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
|
||||
return instanceSupplier;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* 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.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@code RegisteredBean} represents a bean that has been registered with a
|
||||
* {@link BeanFactory}, but has not necessarily been instantiated. It provides
|
||||
* access to the bean factory that contains the bean as well as the bean name.
|
||||
* In the case of inner-beans, the bean name may have been generated.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class RegisteredBean {
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private final Supplier<String> beanName;
|
||||
|
||||
private final boolean generatedBeanName;
|
||||
|
||||
private final Supplier<RootBeanDefinition> mergedBeanDefinition;
|
||||
|
||||
@Nullable
|
||||
private final RegisteredBean parent;
|
||||
|
||||
|
||||
private RegisteredBean(ConfigurableBeanFactory beanFactory, Supplier<String> beanName,
|
||||
boolean generatedBeanName, Supplier<RootBeanDefinition> mergedBeanDefinition,
|
||||
@Nullable RegisteredBean parent) {
|
||||
|
||||
this.beanFactory = beanFactory;
|
||||
this.beanName = beanName;
|
||||
this.generatedBeanName = generatedBeanName;
|
||||
this.mergedBeanDefinition = mergedBeanDefinition;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link RegisteredBean} instance for a regular bean.
|
||||
* @param beanFactory the source bean factory
|
||||
* @param beanName the bean name
|
||||
* @return a new {@link RegisteredBean} instance
|
||||
*/
|
||||
public static RegisteredBean of(ConfigurableBeanFactory beanFactory,
|
||||
String beanName) {
|
||||
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
Assert.hasLength(beanName, "BeanName must not be empty");
|
||||
return new RegisteredBean(beanFactory, () -> beanName, false,
|
||||
() -> (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName),
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link RegisteredBean} instance for an inner-bean.
|
||||
* @param parent the parent of the inner-bean
|
||||
* @param innerBean a {@link BeanDefinitionHolder} for the inner bean
|
||||
* @return a new {@link RegisteredBean} instance
|
||||
*/
|
||||
public static RegisteredBean ofInnerBean(RegisteredBean parent,
|
||||
BeanDefinitionHolder innerBean) {
|
||||
|
||||
Assert.notNull(innerBean, "InnerBean must not be null");
|
||||
return ofInnerBean(parent, innerBean.getBeanName(),
|
||||
innerBean.getBeanDefinition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link RegisteredBean} instance for an inner-bean.
|
||||
* @param parent the parent of the inner-bean
|
||||
* @param innerBeanDefinition the inner-bean definition
|
||||
* @return a new {@link RegisteredBean} instance
|
||||
*/
|
||||
public static RegisteredBean ofInnerBean(RegisteredBean parent,
|
||||
BeanDefinition innerBeanDefinition) {
|
||||
|
||||
return ofInnerBean(parent, null, innerBeanDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link RegisteredBean} instance for an inner-bean.
|
||||
* @param parent the parent of the inner-bean
|
||||
* @param innerBeanName the name of the inner bean or {@code null} to
|
||||
* generate a name
|
||||
* @param innerBeanDefinition the inner-bean definition
|
||||
* @return a new {@link RegisteredBean} instance
|
||||
*/
|
||||
public static RegisteredBean ofInnerBean(RegisteredBean parent,
|
||||
@Nullable String innerBeanName, BeanDefinition innerBeanDefinition) {
|
||||
|
||||
Assert.notNull(parent, "Parent must not be null");
|
||||
Assert.notNull(innerBeanDefinition, "InnerBeanDefinition must not be null");
|
||||
InnerBeanResolver resolver = new InnerBeanResolver(parent, innerBeanName,
|
||||
innerBeanDefinition);
|
||||
Supplier<String> beanName = StringUtils.hasLength(innerBeanName)
|
||||
? () -> innerBeanName : resolver::resolveBeanName;
|
||||
return new RegisteredBean(parent.getBeanFactory(), beanName,
|
||||
innerBeanName == null, resolver::resolveMergedBeanDefinition, parent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the bean.
|
||||
* @return the beanName the bean name
|
||||
*/
|
||||
public String getBeanName() {
|
||||
return this.beanName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the bean name is generated.
|
||||
* @return {@code true} if the name was generated
|
||||
*/
|
||||
public boolean isGeneratedBeanName() {
|
||||
return this.generatedBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean factory containing the bean.
|
||||
* @return the bean factory
|
||||
*/
|
||||
public ConfigurableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user-defined class of the bean.
|
||||
* @return the bean class
|
||||
*/
|
||||
public Class<?> getBeanClass() {
|
||||
if (this.beanFactory.containsSingleton(getBeanName())) {
|
||||
return this.beanFactory.getSingleton(getBeanName()).getClass();
|
||||
}
|
||||
return ClassUtils.getUserClass(getBeanType().toClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ResolvableType} of the bean.
|
||||
* @return the bean type
|
||||
*/
|
||||
public ResolvableType getBeanType() {
|
||||
if (this.beanFactory.containsSingleton(getBeanName())) {
|
||||
return ResolvableType
|
||||
.forInstance(this.beanFactory.getSingleton(getBeanName()));
|
||||
}
|
||||
return getMergedBeanDefinition().getResolvableType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the merged bean definition of the bean.
|
||||
* @return the merged bean definition
|
||||
* @see ConfigurableBeanFactory#getMergedBeanDefinition(String)
|
||||
*/
|
||||
public RootBeanDefinition getMergedBeanDefinition() {
|
||||
return this.mergedBeanDefinition.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this instance is for an inner-bean.
|
||||
* @return if an inner-bean
|
||||
*/
|
||||
public boolean isInnerBean() {
|
||||
return this.parent != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent of this instance or {@code null} if not an inner-bean.
|
||||
* @return the parent
|
||||
*/
|
||||
@Nullable
|
||||
public RegisteredBean getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("beanName", getBeanName())
|
||||
.append("mergedBeanDefinition", getMergedBeanDefinition()).toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolver used to obtain inner-bean details.
|
||||
*/
|
||||
private static class InnerBeanResolver {
|
||||
|
||||
private final RegisteredBean parent;
|
||||
|
||||
@Nullable
|
||||
private final String innerBeanName;
|
||||
|
||||
private final BeanDefinition innerBeanDefinition;
|
||||
|
||||
@Nullable
|
||||
private volatile String resolvedBeanName;
|
||||
|
||||
|
||||
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);
|
||||
this.resolvedBeanName = resolvedBeanName;
|
||||
return resolvedBeanName;
|
||||
}
|
||||
|
||||
RootBeanDefinition resolveMergedBeanDefinition() {
|
||||
return resolveInnerBean(
|
||||
(beanName, mergedBeanDefinition) -> mergedBeanDefinition);
|
||||
}
|
||||
|
||||
private <T> T resolveInnerBean(
|
||||
BiFunction<String, RootBeanDefinition, T> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link AbstractAutowireCapableBeanFactory} instance supplier
|
||||
* support.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class BeanFactorySupplierTests {
|
||||
|
||||
@Test
|
||||
void getBeanWhenUsingRegularSupplier() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setInstanceSupplier(() -> "I am supplied");
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
assertThat(beanFactory.getBean("test")).isEqualTo("I am supplied");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanWhenUsingInstanceSupplier() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setInstanceSupplier(InstanceSupplier
|
||||
.of(registeredBean -> "I am bean " + registeredBean.getBeanName()));
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
assertThat(beanFactory.getBean("test")).isEqualTo("I am bean test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanWhenUsingThrowableSupplier() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setInstanceSupplier(ThrowingSupplier.of(() -> "I am supplied"));
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
assertThat(beanFactory.getBean("test")).isEqualTo("I am supplied");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanWhenUsingThrowableSupplierThatThrowsCheckedException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setInstanceSupplier(ThrowingSupplier.of(() -> {
|
||||
throw new IOException("fail");
|
||||
}));
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> beanFactory.getBean("test"))
|
||||
.withCauseInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanWhenUsingThrowableSupplierThatThrowsRuntimeException() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setInstanceSupplier(ThrowingSupplier.of(() -> {
|
||||
throw new IllegalStateException("fail");
|
||||
}));
|
||||
beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> beanFactory.getBean("test"))
|
||||
.withCauseInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.function.ThrowingBiFunction;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
||||
/**
|
||||
* Tests for {@link InstanceSupplier}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class InstanceSupplierTests {
|
||||
|
||||
private final RegisteredBean registeredBean = RegisteredBean
|
||||
.of(new DefaultListableBeanFactory(), "test");
|
||||
|
||||
@Test
|
||||
void getWithoutRegisteredBeanThrowsException() {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "test";
|
||||
assertThatIllegalStateException().isThrownBy(() -> supplier.get())
|
||||
.withMessage("No RegisteredBean parameter provided");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWithExceptionWithoutRegisteredBeanThrowsException() {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "test";
|
||||
assertThatIllegalStateException().isThrownBy(() -> supplier.getWithException())
|
||||
.withMessage("No RegisteredBean parameter provided");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReturnsResult() throws Exception {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "test";
|
||||
assertThat(supplier.get(this.registeredBean)).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void andThenWithBiFunctionWhenFunctionIsNullThrowsException() {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "test";
|
||||
ThrowingBiFunction<RegisteredBean, String, String> after = null;
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> supplier.andThen(after))
|
||||
.withMessage("After must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void andThenWithBiFunctionAppliesFunctionToObtainResult() throws Exception {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "bean";
|
||||
supplier = supplier.andThen(
|
||||
(registeredBean, string) -> registeredBean.getBeanName() + "-" + string);
|
||||
assertThat(supplier.get(this.registeredBean)).isEqualTo("test-bean");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofSupplierWhenInstanceSupplierReturnsSameInstance() {
|
||||
InstanceSupplier<String> supplier = registeredBean -> "test";
|
||||
assertThat(InstanceSupplier.of(supplier)).isSameAs(supplier);
|
||||
}
|
||||
|
||||
@Test
|
||||
void usingSupplierAdaptsToInstanceSupplier() throws Exception {
|
||||
InstanceSupplier<String> instanceSupplier = InstanceSupplier.using(() -> "test");
|
||||
assertThat(instanceSupplier.get(this.registeredBean)).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofInstanceSupplierAdaptsToInstanceSupplier() throws Exception {
|
||||
InstanceSupplier<String> instanceSupplier = InstanceSupplier
|
||||
.of(registeredBean -> "test");
|
||||
assertThat(instanceSupplier.get(this.registeredBean)).isEqualTo("test");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* 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 org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link RegisteredBean}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class RegisteredBeanTests {
|
||||
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.beanFactory = new DefaultListableBeanFactory();
|
||||
this.beanFactory.registerBeanDefinition("bd",
|
||||
new RootBeanDefinition(TestBean.class));
|
||||
this.beanFactory.registerSingleton("sb", new TestBean());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenBeanFactoryIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RegisteredBean.of(null, "bd"))
|
||||
.withMessage("BeanFactory must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenBeanNameIsEmptyThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RegisteredBean.of(this.beanFactory, null))
|
||||
.withMessage("BeanName must not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofInnerBeanWhenInnerBeanIsNullThrowsException() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> RegisteredBean.ofInnerBean(parent, (BeanDefinitionHolder) null))
|
||||
.withMessage("InnerBean must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofInnerBeanWhenParentIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RegisteredBean.ofInnerBean(null,
|
||||
new RootBeanDefinition(TestInnerBean.class)))
|
||||
.withMessage("Parent must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofInnerBeanWhenInnerBeanDefinitionIsNullThrowsException() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> RegisteredBean.ofInnerBean(parent, "ib", null))
|
||||
.withMessage("InnerBeanDefinition must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameReturnsBeanName() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.getBeanName()).isEqualTo("bd");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameWhenNamedInnerBeanReturnsBeanName() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent, "ib",
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.getBeanName()).isEqualTo("ib");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanNameWhenUnnamedInnerBeanReturnsBeanName() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.getBeanName()).startsWith("(inner bean)#");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanClassReturnsBeanClass() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.getBeanClass()).isEqualTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanClassWhenSingletonReturnsBeanClass() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "sb");
|
||||
assertThat(registeredBean.getBeanClass()).isEqualTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanTypeReturnsBeanType() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.getBeanType().toClass()).isEqualTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanTypeWhenSingletonReturnsBeanType() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "sb");
|
||||
assertThat(registeredBean.getBeanType().toClass()).isEqualTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedBeanDefinitionReturnsMergedBeanDefinition() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.getMergedBeanDefinition().getBeanClass())
|
||||
.isEqualTo(TestBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedBeanDefinitionWhenSingletonThrowsException() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "sb");
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
|
||||
.isThrownBy(() -> registeredBean.getMergedBeanDefinition());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMergedBeanDefinitionWhenInnerBeanReturnsMergedBeanDefinition() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.getMergedBeanDefinition().getBeanClass())
|
||||
.isEqualTo(TestInnerBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInnerBeanWhenInnerBeanReturnsTrue() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.isInnerBean()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInnerBeanWhenNotInnerBeanReturnsTrue() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.isInnerBean()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getParentWhenInnerBeanReturnsParent() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.getParent()).isSameAs(parent);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getParentWhenNotInnerBeanReturnsNull() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.getParent()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isGeneratedBeanNameWhenInnerBeanWithoutNameReturnsTrue() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new RootBeanDefinition(TestInnerBean.class));
|
||||
assertThat(registeredBean.isGeneratedBeanName()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isGeneratedBeanNameWhenInnerBeanWithNameReturnsFalse() {
|
||||
RegisteredBean parent = RegisteredBean.of(this.beanFactory, "bd");
|
||||
RegisteredBean registeredBean = RegisteredBean.ofInnerBean(parent,
|
||||
new BeanDefinitionHolder(new RootBeanDefinition(TestInnerBean.class),
|
||||
"test"));
|
||||
assertThat(registeredBean.isGeneratedBeanName()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isGeneratedBeanNameWhenNotInnerBeanReturnsFalse() {
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "bd");
|
||||
assertThat(registeredBean.isGeneratedBeanName()).isFalse();
|
||||
}
|
||||
|
||||
static class TestBean {
|
||||
|
||||
}
|
||||
|
||||
static class TestInnerBean {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue