Dependency tracking for Supplier-created beans
Issue: SPR-15417
This commit is contained in:
parent
ea5cb26d75
commit
a95843a068
|
@ -73,6 +73,7 @@ import org.springframework.beans.factory.config.TypedStringValue;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.NamedThreadLocal;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.PriorityOrdered;
|
import org.springframework.core.PriorityOrdered;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
@ -146,6 +147,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
*/
|
*/
|
||||||
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
|
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the currently created bean, for implicit dependency registration
|
||||||
|
* on getBean etc invocations triggered from a user-specified Supplier callback.
|
||||||
|
*/
|
||||||
|
private final NamedThreadLocal<String> currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");
|
||||||
|
|
||||||
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
|
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
|
||||||
private final Map<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>(16);
|
private final Map<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>(16);
|
||||||
|
|
||||||
|
@ -1062,7 +1069,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
* @param mbd the bean definition for the bean
|
* @param mbd the bean definition for the bean
|
||||||
* @param args explicit arguments to use for constructor or factory method invocation
|
* @param args explicit arguments to use for constructor or factory method invocation
|
||||||
* @return BeanWrapper for the new instance
|
* @return a BeanWrapper for the new instance
|
||||||
|
* @see #obtainFromSupplier
|
||||||
* @see #instantiateUsingFactoryMethod
|
* @see #instantiateUsingFactoryMethod
|
||||||
* @see #autowireConstructor
|
* @see #autowireConstructor
|
||||||
* @see #instantiateBean
|
* @see #instantiateBean
|
||||||
|
@ -1078,9 +1086,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
|
|
||||||
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
|
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
|
||||||
if (instanceSupplier != null) {
|
if (instanceSupplier != null) {
|
||||||
BeanWrapper bw = new BeanWrapperImpl(instanceSupplier.get());
|
return obtainFromSupplier(instanceSupplier, beanName);
|
||||||
initBeanWrapper(bw);
|
|
||||||
return bw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mbd.getFactoryMethodName() != null) {
|
if (mbd.getFactoryMethodName() != null) {
|
||||||
|
@ -1119,6 +1125,53 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
return instantiateBean(beanName, mbd);
|
return instantiateBean(beanName, mbd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain a bean instance from the given supplier.
|
||||||
|
* @param instanceSupplier 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) {
|
||||||
|
String outerBean = this.currentlyCreatedBean.get();
|
||||||
|
this.currentlyCreatedBean.set(beanName);
|
||||||
|
Object instance;
|
||||||
|
try {
|
||||||
|
instance = instanceSupplier.get();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (outerBean != null) {
|
||||||
|
this.currentlyCreatedBean.set(outerBean);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentlyCreatedBean.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BeanWrapper bw = new BeanWrapperImpl(instance);
|
||||||
|
initBeanWrapper(bw);
|
||||||
|
return bw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden in order to implicitly register the currently created bean as
|
||||||
|
* dependent on further beans getting programmatically retrieved during a
|
||||||
|
* {@link Supplier} callback.
|
||||||
|
* @since 5.0
|
||||||
|
* @see #obtainFromSupplier
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Object getObjectForBeanInstance(
|
||||||
|
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
|
||||||
|
|
||||||
|
String currentlyCreatedBean = this.currentlyCreatedBean.get();
|
||||||
|
if (currentlyCreatedBean != null) {
|
||||||
|
registerDependentBean(beanName, currentlyCreatedBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine candidate constructors to use for the given bean, checking all registered
|
* Determine candidate constructors to use for the given bean, checking all registered
|
||||||
* {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
|
* {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
|
||||||
|
@ -1149,7 +1202,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
* Instantiate the given bean using its default constructor.
|
* Instantiate the given bean using its default constructor.
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
* @param mbd the bean definition for the bean
|
* @param mbd the bean definition for the bean
|
||||||
* @return BeanWrapper for the new instance
|
* @return a BeanWrapper for the new instance
|
||||||
*/
|
*/
|
||||||
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
|
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
|
||||||
try {
|
try {
|
||||||
|
@ -1184,7 +1237,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
* @param mbd the bean definition for the bean
|
* @param mbd the bean definition for the bean
|
||||||
* @param explicitArgs argument values passed in programmatically via the getBean method,
|
* @param explicitArgs argument values passed in programmatically via the getBean method,
|
||||||
* or {@code null} if none (-> use constructor argument values from bean definition)
|
* or {@code null} if none (-> use constructor argument values from bean definition)
|
||||||
* @return BeanWrapper for the new instance
|
* @return a BeanWrapper for the new instance
|
||||||
* @see #getBean(String, Object[])
|
* @see #getBean(String, Object[])
|
||||||
*/
|
*/
|
||||||
protected BeanWrapper instantiateUsingFactoryMethod(
|
protected BeanWrapper instantiateUsingFactoryMethod(
|
||||||
|
@ -1205,7 +1258,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
* @param ctors the chosen candidate constructors
|
* @param ctors the chosen candidate constructors
|
||||||
* @param explicitArgs argument values passed in programmatically via the getBean method,
|
* @param explicitArgs argument values passed in programmatically via the getBean method,
|
||||||
* or {@code null} if none (-> use constructor argument values from bean definition)
|
* or {@code null} if none (-> use constructor argument values from bean definition)
|
||||||
* @return BeanWrapper for the new instance
|
* @return a BeanWrapper for the new instance
|
||||||
*/
|
*/
|
||||||
protected BeanWrapper autowireConstructor(
|
protected BeanWrapper autowireConstructor(
|
||||||
String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {
|
String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -156,6 +156,11 @@ public class AnnotationConfigApplicationContextTests {
|
||||||
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
|
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
|
||||||
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
|
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
|
||||||
assertSame(context, context.getBean(BeanB.class).applicationContext);
|
assertSame(context, context.getBean(BeanB.class).applicationContext);
|
||||||
|
|
||||||
|
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
|
||||||
|
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanB"));
|
||||||
|
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
|
||||||
|
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanC"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue