Skip to content

Commit 80e7ee3

Browse files
committed
Mark bootstrap thread for entire finishBeanFactoryInitialization phase
Closes gh-35398
1 parent ecd3dd8 commit 80e7ee3

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,26 @@ boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
152152
*/
153153
boolean isConfigurationFrozen();
154154

155+
/**
156+
* Mark current thread as main bootstrap thread for singleton instantiation,
157+
* with lenient bootstrap locking applying for background threads.
158+
* <p>Any such marker is to be removed at the end of the managed bootstrap in
159+
* {@link #preInstantiateSingletons()}.
160+
* @since 6.2.12
161+
* @see #setBootstrapExecutor
162+
* @see #preInstantiateSingletons()
163+
*/
164+
default void prepareSingletonBootstrap() {
165+
}
166+
155167
/**
156168
* Ensure that all non-lazy-init singletons are instantiated, also considering
157169
* {@link org.springframework.beans.factory.FactoryBean FactoryBeans}.
158170
* Typically invoked at the end of factory setup, if desired.
159171
* @throws BeansException if one of the singleton beans could not be created.
160172
* Note: This may have left the factory with some beans already initialized!
161173
* Call {@link #destroySingletons()} for full cleanup in this case.
174+
* @see #prepareSingletonBootstrap()
162175
* @see #destroySingletons()
163176
*/
164177
void preInstantiateSingletons() throws BeansException;

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,11 @@ else if (this.strictLocking == null) {
11021102
return null;
11031103
}
11041104

1105+
@Override
1106+
public void prepareSingletonBootstrap() {
1107+
this.mainThreadPrefix = getThreadNamePrefix();
1108+
}
1109+
11051110
@Override
11061111
public void preInstantiateSingletons() throws BeansException {
11071112
if (logger.isTraceEnabled()) {
@@ -1114,7 +1119,9 @@ public void preInstantiateSingletons() throws BeansException {
11141119

11151120
// Trigger initialization of all non-lazy singleton beans...
11161121
this.preInstantiationThread.set(PreInstantiation.MAIN);
1117-
this.mainThreadPrefix = getThreadNamePrefix();
1122+
if (this.mainThreadPrefix == null) {
1123+
this.mainThreadPrefix = getThreadNamePrefix();
1124+
}
11181125
try {
11191126
List<CompletableFuture<?>> futures = new ArrayList<>();
11201127
for (String beanName : beanNames) {

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,9 @@ protected void registerListeners() {
936936
*/
937937
@SuppressWarnings("unchecked")
938938
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
939+
// Mark current thread for singleton instantiation with applied bootstrap locking.
940+
beanFactory.prepareSingletonBootstrap();
941+
939942
// Initialize bootstrap executor for this context.
940943
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
941944
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3232
import org.springframework.beans.testfixture.beans.TestBean;
3333
import org.springframework.context.ConfigurableApplicationContext;
34+
import org.springframework.context.weaving.LoadTimeWeaverAware;
3435
import org.springframework.core.SpringProperties;
3536
import org.springframework.core.testfixture.EnabledForTestGroups;
3637
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@@ -68,6 +69,16 @@ void bootstrapWithUnmanagedThreads() {
6869
ctx.close();
6970
}
7071

72+
@Test
73+
@Timeout(10)
74+
@EnabledForTestGroups(LONG_RUNNING)
75+
void bootstrapWithLoadTimeWeaverAware() {
76+
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(LoadTimeWeaverAwareBeanConfig.class);
77+
ctx.getBean("testBean1", TestBean.class);
78+
ctx.getBean("testBean2", TestBean.class);
79+
ctx.close();
80+
}
81+
7182
@Test
7283
@Timeout(10)
7384
@EnabledForTestGroups(LONG_RUNNING)
@@ -266,6 +277,50 @@ public Class<?> getObjectType() {
266277
}
267278

268279

280+
@Configuration(proxyBeanMethods = false)
281+
static class LoadTimeWeaverAwareBeanConfig {
282+
283+
@Bean
284+
LoadTimeWeaverAware loadTimeWeaverAware(ObjectProvider<TestBean> testBean1) {
285+
Thread thread = new Thread(testBean1::getObject);
286+
thread.start();
287+
try {
288+
thread.join();
289+
}
290+
catch (InterruptedException ex) {
291+
Thread.currentThread().interrupt();
292+
}
293+
return (loadTimeWeaver -> {});
294+
}
295+
296+
@Bean
297+
public TestBean testBean1(TestBean testBean2) {
298+
return new TestBean(testBean2);
299+
}
300+
301+
@Bean @Lazy
302+
public FactoryBean<TestBean> testBean2() {
303+
try {
304+
Thread.sleep(2000);
305+
}
306+
catch (InterruptedException ex) {
307+
Thread.currentThread().interrupt();
308+
}
309+
TestBean testBean = new TestBean();
310+
return new FactoryBean<>() {
311+
@Override
312+
public TestBean getObject() {
313+
return testBean;
314+
}
315+
@Override
316+
public Class<?> getObjectType() {
317+
return testBean.getClass();
318+
}
319+
};
320+
}
321+
}
322+
323+
269324
@Configuration(proxyBeanMethods = false)
270325
static class StrictLockingBeanConfig {
271326

0 commit comments

Comments
 (0)