Skip to content

Commit a95843a

Browse files
committed
Dependency tracking for Supplier-created beans
Issue: SPR-15417
1 parent ea5cb26 commit a95843a

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.springframework.core.DefaultParameterNameDiscoverer;
7474
import org.springframework.core.GenericTypeResolver;
7575
import org.springframework.core.MethodParameter;
76+
import org.springframework.core.NamedThreadLocal;
7677
import org.springframework.core.ParameterNameDiscoverer;
7778
import org.springframework.core.PriorityOrdered;
7879
import org.springframework.core.ResolvableType;
@@ -146,6 +147,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
146147
*/
147148
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
148149

150+
/**
151+
* The name of the currently created bean, for implicit dependency registration
152+
* on getBean etc invocations triggered from a user-specified Supplier callback.
153+
*/
154+
private final NamedThreadLocal<String> currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");
155+
149156
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
150157
private final Map<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>(16);
151158

@@ -1062,7 +1069,8 @@ protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass,
10621069
* @param beanName the name of the bean
10631070
* @param mbd the bean definition for the bean
10641071
* @param args explicit arguments to use for constructor or factory method invocation
1065-
* @return BeanWrapper for the new instance
1072+
* @return a BeanWrapper for the new instance
1073+
* @see #obtainFromSupplier
10661074
* @see #instantiateUsingFactoryMethod
10671075
* @see #autowireConstructor
10681076
* @see #instantiateBean
@@ -1078,9 +1086,7 @@ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd
10781086

10791087
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
10801088
if (instanceSupplier != null) {
1081-
BeanWrapper bw = new BeanWrapperImpl(instanceSupplier.get());
1082-
initBeanWrapper(bw);
1083-
return bw;
1089+
return obtainFromSupplier(instanceSupplier, beanName);
10841090
}
10851091

10861092
if (mbd.getFactoryMethodName() != null) {
@@ -1119,6 +1125,53 @@ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd
11191125
return instantiateBean(beanName, mbd);
11201126
}
11211127

1128+
/**
1129+
* Obtain a bean instance from the given supplier.
1130+
* @param instanceSupplier the configured supplier
1131+
* @param beanName the corresponding bean name
1132+
* @return a BeanWrapper for the new instance
1133+
* @since 5.0
1134+
* @see #getObjectForBeanInstance
1135+
*/
1136+
protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
1137+
String outerBean = this.currentlyCreatedBean.get();
1138+
this.currentlyCreatedBean.set(beanName);
1139+
Object instance;
1140+
try {
1141+
instance = instanceSupplier.get();
1142+
}
1143+
finally {
1144+
if (outerBean != null) {
1145+
this.currentlyCreatedBean.set(outerBean);
1146+
}
1147+
else {
1148+
this.currentlyCreatedBean.remove();
1149+
}
1150+
}
1151+
BeanWrapper bw = new BeanWrapperImpl(instance);
1152+
initBeanWrapper(bw);
1153+
return bw;
1154+
}
1155+
1156+
/**
1157+
* Overridden in order to implicitly register the currently created bean as
1158+
* dependent on further beans getting programmatically retrieved during a
1159+
* {@link Supplier} callback.
1160+
* @since 5.0
1161+
* @see #obtainFromSupplier
1162+
*/
1163+
@Override
1164+
protected Object getObjectForBeanInstance(
1165+
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
1166+
1167+
String currentlyCreatedBean = this.currentlyCreatedBean.get();
1168+
if (currentlyCreatedBean != null) {
1169+
registerDependentBean(beanName, currentlyCreatedBean);
1170+
}
1171+
1172+
return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
1173+
}
1174+
11221175
/**
11231176
* Determine candidate constructors to use for the given bean, checking all registered
11241177
* {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
@@ -1149,7 +1202,7 @@ protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(Class<?>
11491202
* Instantiate the given bean using its default constructor.
11501203
* @param beanName the name of the bean
11511204
* @param mbd the bean definition for the bean
1152-
* @return BeanWrapper for the new instance
1205+
* @return a BeanWrapper for the new instance
11531206
*/
11541207
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
11551208
try {
@@ -1184,7 +1237,7 @@ public Object run() {
11841237
* @param mbd the bean definition for the bean
11851238
* @param explicitArgs argument values passed in programmatically via the getBean method,
11861239
* or {@code null} if none (-> use constructor argument values from bean definition)
1187-
* @return BeanWrapper for the new instance
1240+
* @return a BeanWrapper for the new instance
11881241
* @see #getBean(String, Object[])
11891242
*/
11901243
protected BeanWrapper instantiateUsingFactoryMethod(
@@ -1205,7 +1258,7 @@ protected BeanWrapper instantiateUsingFactoryMethod(
12051258
* @param ctors the chosen candidate constructors
12061259
* @param explicitArgs argument values passed in programmatically via the getBean method,
12071260
* or {@code null} if none (-> use constructor argument values from bean definition)
1208-
* @return BeanWrapper for the new instance
1261+
* @return a BeanWrapper for the new instance
12091262
*/
12101263
protected BeanWrapper autowireConstructor(
12111264
String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {

spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -156,6 +156,11 @@ public void individualBeanWithSupplier() {
156156
assertSame(context.getBean(BeanB.class), context.getBean(BeanA.class).b);
157157
assertSame(context.getBean(BeanC.class), context.getBean(BeanA.class).c);
158158
assertSame(context, context.getBean(BeanB.class).applicationContext);
159+
160+
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
161+
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanB"));
162+
assertArrayEquals(new String[] {"annotationConfigApplicationContextTests.BeanA"},
163+
context.getDefaultListableBeanFactory().getDependentBeans("annotationConfigApplicationContextTests.BeanC"));
159164
}
160165

161166
@Test

0 commit comments

Comments
 (0)