@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2024 the original author or authors .
* Copyright 2002 - 2025 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 .
@ -19,11 +19,14 @@ package org.springframework.context.annotation;
@@ -19,11 +19,14 @@ package org.springframework.context.annotation;
import org.junit.jupiter.api.Test ;
import org.junit.jupiter.api.Timeout ;
import org.springframework.beans.factory.BeanCurrentlyInCreationException ;
import org.springframework.beans.factory.ObjectProvider ;
import org.springframework.beans.testfixture.beans.TestBean ;
import org.springframework.context.ConfigurableApplicationContext ;
import org.springframework.core.testfixture.EnabledForTestGroups ;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor ;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType ;
import static org.springframework.context.annotation.Bean.Bootstrap.BACKGROUND ;
import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING ;
@ -33,6 +36,17 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
@@ -33,6 +36,17 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING;
* /
class BackgroundBootstrapTests {
@Test
@Timeout ( 5 )
@EnabledForTestGroups ( LONG_RUNNING )
void bootstrapWithUnmanagedThread ( ) {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext ( UnmanagedThreadBeanConfig . class ) ;
ctx . getBean ( "testBean1" , TestBean . class ) ;
assertThatExceptionOfType ( BeanCurrentlyInCreationException . class ) . isThrownBy ( // late - not during refresh
( ) - > ctx . getBean ( "testBean2" , TestBean . class ) ) ;
ctx . close ( ) ;
}
@Test
@Timeout ( 5 )
@EnabledForTestGroups ( LONG_RUNNING )
@ -45,7 +59,35 @@ class BackgroundBootstrapTests {
@@ -45,7 +59,35 @@ class BackgroundBootstrapTests {
}
@Configuration
@Configuration ( proxyBeanMethods = false )
static class UnmanagedThreadBeanConfig {
@Bean
public TestBean testBean1 ( ObjectProvider < TestBean > testBean2 ) {
new Thread ( testBean2 : : getObject ) . start ( ) ;
try {
Thread . sleep ( 1000 ) ;
}
catch ( InterruptedException ex ) {
throw new RuntimeException ( ex ) ;
}
return new TestBean ( ) ;
}
@Bean
public TestBean testBean2 ( ) {
try {
Thread . sleep ( 2000 ) ;
}
catch ( InterruptedException ex ) {
throw new RuntimeException ( ex ) ;
}
return new TestBean ( ) ;
}
}
@Configuration ( proxyBeanMethods = false )
static class CustomExecutorBeanConfig {
@Bean
@ -58,7 +100,7 @@ class BackgroundBootstrapTests {
@@ -58,7 +100,7 @@ class BackgroundBootstrapTests {
}
@Bean ( bootstrap = BACKGROUND ) @DependsOn ( "testBean3" )
public TestBean testBean1 ( TestBean testBean3 ) throws InterruptedException {
public TestBean testBean1 ( TestBean testBean3 ) throws InterruptedException {
Thread . sleep ( 3000 ) ;
return new TestBean ( ) ;
}