1919import org .junit .jupiter .api .Test ;
2020import org .junit .jupiter .api .Timeout ;
2121
22+ import org .springframework .beans .factory .BeanCreationException ;
2223import org .springframework .beans .factory .BeanCurrentlyInCreationException ;
2324import org .springframework .beans .factory .ObjectProvider ;
2425import org .springframework .beans .factory .UnsatisfiedDependencyException ;
4243class BackgroundBootstrapTests {
4344
4445 @ Test
45- @ Timeout (5 )
46+ @ Timeout (10 )
4647 @ EnabledForTestGroups (LONG_RUNNING )
4748 void bootstrapWithUnmanagedThread () {
4849 ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext (UnmanagedThreadBeanConfig .class );
@@ -52,7 +53,7 @@ void bootstrapWithUnmanagedThread() {
5253 }
5354
5455 @ Test
55- @ Timeout (5 )
56+ @ Timeout (10 )
5657 @ EnabledForTestGroups (LONG_RUNNING )
5758 void bootstrapWithUnmanagedThreads () {
5859 ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext (UnmanagedThreadsBeanConfig .class );
@@ -64,7 +65,7 @@ void bootstrapWithUnmanagedThreads() {
6465 }
6566
6667 @ Test
67- @ Timeout (5 )
68+ @ Timeout (10 )
6869 @ EnabledForTestGroups (LONG_RUNNING )
6970 void bootstrapWithStrictLockingThread () {
7071 SpringProperties .setFlag (DefaultListableBeanFactory .STRICT_LOCKING_PROPERTY_NAME );
@@ -79,17 +80,26 @@ void bootstrapWithStrictLockingThread() {
7980 }
8081
8182 @ Test
82- @ Timeout (5 )
83+ @ Timeout (10 )
8384 @ EnabledForTestGroups (LONG_RUNNING )
84- void bootstrapWithCircularReference () {
85- ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext (CircularReferenceBeanConfig .class );
85+ void bootstrapWithCircularReferenceAgainstMainThread () {
86+ ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext (CircularReferenceAgainstMainThreadBeanConfig .class );
8687 ctx .getBean ("testBean1" , TestBean .class );
8788 ctx .getBean ("testBean2" , TestBean .class );
8889 ctx .close ();
8990 }
9091
9192 @ Test
92- @ Timeout (5 )
93+ @ Timeout (10 )
94+ @ EnabledForTestGroups (LONG_RUNNING )
95+ void bootstrapWithCircularReferenceWithBlockingMainThread () {
96+ assertThatExceptionOfType (BeanCreationException .class )
97+ .isThrownBy (() -> new AnnotationConfigApplicationContext (CircularReferenceWithBlockingMainThreadBeanConfig .class ))
98+ .withRootCauseInstanceOf (BeanCurrentlyInCreationException .class );
99+ }
100+
101+ @ Test
102+ @ Timeout (10 )
93103 @ EnabledForTestGroups (LONG_RUNNING )
94104 void bootstrapWithCircularReferenceInSameThread () {
95105 assertThatExceptionOfType (UnsatisfiedDependencyException .class )
@@ -98,7 +108,16 @@ void bootstrapWithCircularReferenceInSameThread() {
98108 }
99109
100110 @ Test
101- @ Timeout (5 )
111+ @ Timeout (10 )
112+ @ EnabledForTestGroups (LONG_RUNNING )
113+ void bootstrapWithCircularReferenceInMultipleThreads () {
114+ assertThatExceptionOfType (BeanCreationException .class )
115+ .isThrownBy (() -> new AnnotationConfigApplicationContext (CircularReferenceInMultipleThreadsBeanConfig .class ))
116+ .withRootCauseInstanceOf (BeanCurrentlyInCreationException .class );
117+ }
118+
119+ @ Test
120+ @ Timeout (10 )
102121 @ EnabledForTestGroups (LONG_RUNNING )
103122 void bootstrapWithCustomExecutor () {
104123 ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext (CustomExecutorBeanConfig .class );
@@ -202,7 +221,7 @@ public TestBean testBean2(ConfigurableListableBeanFactory beanFactory) {
202221
203222
204223 @ Configuration (proxyBeanMethods = false )
205- static class CircularReferenceBeanConfig {
224+ static class CircularReferenceAgainstMainThreadBeanConfig {
206225
207226 @ Bean
208227 public TestBean testBean1 (ObjectProvider <TestBean > testBean2 ) {
@@ -229,6 +248,46 @@ public TestBean testBean2(TestBean testBean1) {
229248 }
230249
231250
251+ @ Configuration (proxyBeanMethods = false )
252+ static class CircularReferenceWithBlockingMainThreadBeanConfig {
253+
254+ @ Bean
255+ public TestBean testBean1 (ObjectProvider <TestBean > testBean2 ) {
256+ Thread thread = new Thread (testBean2 ::getObject );
257+ thread .setUncaughtExceptionHandler ((t , ex ) -> System .out .println (System .currentTimeMillis () + " " + ex + " " + t ));
258+ thread .start ();
259+ try {
260+ Thread .sleep (1000 );
261+ }
262+ catch (InterruptedException ex ) {
263+ throw new RuntimeException (ex );
264+ }
265+ return new TestBean (testBean2 .getObject ());
266+ }
267+
268+ @ Bean
269+ public TestBean testBean2 (ObjectProvider <TestBean > testBean1 ) {
270+ System .out .println (System .currentTimeMillis () + " testBean2 begin " + Thread .currentThread ());
271+ try {
272+ Thread .sleep (2000 );
273+ }
274+ catch (InterruptedException ex ) {
275+ throw new RuntimeException (ex );
276+ }
277+ try {
278+ return new TestBean (testBean1 .getObject ());
279+ }
280+ catch (RuntimeException ex ) {
281+ System .out .println (System .currentTimeMillis () + " testBean2 exception " + Thread .currentThread ());
282+ throw ex ;
283+ }
284+ finally {
285+ System .out .println (System .currentTimeMillis () + " testBean2 end " + Thread .currentThread ());
286+ }
287+ }
288+ }
289+
290+
232291 @ Configuration (proxyBeanMethods = false )
233292 static class CircularReferenceInSameThreadBeanConfig {
234293
@@ -262,6 +321,46 @@ public TestBean testBean3(TestBean testBean2) {
262321 }
263322
264323
324+ @ Configuration (proxyBeanMethods = false )
325+ static class CircularReferenceInMultipleThreadsBeanConfig {
326+
327+ @ Bean
328+ public TestBean testBean1 (ObjectProvider <TestBean > testBean2 , ObjectProvider <TestBean > testBean3 ) {
329+ new Thread (testBean2 ::getObject ).start ();
330+ new Thread (testBean3 ::getObject ).start ();
331+ try {
332+ Thread .sleep (2000 );
333+ }
334+ catch (InterruptedException ex ) {
335+ throw new RuntimeException (ex );
336+ }
337+ return new TestBean ();
338+ }
339+
340+ @ Bean
341+ public TestBean testBean2 (ObjectProvider <TestBean > testBean3 ) {
342+ try {
343+ Thread .sleep (1000 );
344+ }
345+ catch (InterruptedException ex ) {
346+ throw new RuntimeException (ex );
347+ }
348+ return new TestBean (testBean3 .getObject ());
349+ }
350+
351+ @ Bean
352+ public TestBean testBean3 (ObjectProvider <TestBean > testBean2 ) {
353+ try {
354+ Thread .sleep (1000 );
355+ }
356+ catch (InterruptedException ex ) {
357+ throw new RuntimeException (ex );
358+ }
359+ return new TestBean (testBean2 .getObject ());
360+ }
361+ }
362+
363+
265364 @ Configuration (proxyBeanMethods = false )
266365 static class CustomExecutorBeanConfig {
267366
0 commit comments