diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 00867c7d415..056481a86db 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -315,7 +315,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements while ((singletonObject = this.singletonObjects.get(beanName)) == null) { Thread otherThread = this.currentCreationThreads.get(beanName); if (otherThread != null && (otherThread == currentThread || - this.lenientWaitingThreads.get(otherThread) == currentThread)) { + checkDependentWaitingThreads(otherThread, currentThread))) { throw ex; } if (!this.singletonsInLenientCreation.contains(beanName)) { @@ -431,6 +431,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements } } + private boolean checkDependentWaitingThreads(Thread waitingThread, Thread candidateThread) { + Thread threadToCheck = waitingThread; + while ((threadToCheck = this.lenientWaitingThreads.get(threadToCheck)) != null) { + if (threadToCheck == candidateThread) { + return true; + } + } + return false; + } + /** * Determine whether the current thread is allowed to hold the singleton lock. *

By default, any thread may acquire and hold the singleton lock, except diff --git a/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java b/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java index 3d7662ec44e..a0731445314 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java @@ -139,7 +139,7 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -150,7 +150,7 @@ class BackgroundBootstrapTests { Thread.sleep(2000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -170,7 +170,7 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -191,7 +191,7 @@ class BackgroundBootstrapTests { Thread.sleep(2000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -208,7 +208,7 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean("testBean1"); } @@ -230,7 +230,7 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -241,7 +241,7 @@ class BackgroundBootstrapTests { Thread.sleep(2000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -253,37 +253,25 @@ class BackgroundBootstrapTests { @Bean public TestBean testBean1(ObjectProvider testBean2) { - Thread thread = new Thread(testBean2::getObject); - thread.setUncaughtExceptionHandler((t, ex) -> System.out.println(System.currentTimeMillis() + " " + ex + " " + t)); - thread.start(); + new Thread(testBean2::getObject).start(); try { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(testBean2.getObject()); } @Bean public TestBean testBean2(ObjectProvider testBean1) { - System.out.println(System.currentTimeMillis() + " testBean2 begin " + Thread.currentThread()); try { Thread.sleep(2000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - try { - return new TestBean(testBean1.getObject()); - } - catch (RuntimeException ex) { - System.out.println(System.currentTimeMillis() + " testBean2 exception " + Thread.currentThread()); - throw ex; - } - finally { - System.out.println(System.currentTimeMillis() + " testBean2 end " + Thread.currentThread()); + Thread.currentThread().interrupt(); } + return new TestBean(testBean1.getObject()); } } @@ -298,7 +286,7 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -309,7 +297,7 @@ class BackgroundBootstrapTests { Thread.sleep(2000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -325,14 +313,17 @@ class BackgroundBootstrapTests { static class CircularReferenceInMultipleThreadsBeanConfig { @Bean - public TestBean testBean1(ObjectProvider testBean2, ObjectProvider testBean3) { + public TestBean testBean1(ObjectProvider testBean2, ObjectProvider testBean3, + ObjectProvider testBean4) { + new Thread(testBean2::getObject).start(); new Thread(testBean3::getObject).start(); + new Thread(testBean4::getObject).start(); try { - Thread.sleep(2000); + Thread.sleep(3000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(); } @@ -343,18 +334,29 @@ class BackgroundBootstrapTests { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(testBean3.getObject()); } @Bean - public TestBean testBean3(ObjectProvider testBean2) { + public TestBean testBean3(ObjectProvider testBean4) { + try { + Thread.sleep(1000); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + return new TestBean(testBean4.getObject()); + } + + @Bean + public TestBean testBean4(ObjectProvider testBean2) { try { Thread.sleep(1000); } catch (InterruptedException ex) { - throw new RuntimeException(ex); + Thread.currentThread().interrupt(); } return new TestBean(testBean2.getObject()); }