diff --git a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java index 7b84e3e09cc..d93ac4b4143 100644 --- a/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java +++ b/buildSrc/src/main/java/org/springframework/build/CheckstyleConventions.java @@ -50,7 +50,7 @@ public class CheckstyleConventions { project.getPlugins().apply(CheckstylePlugin.class); project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g")); CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class); - checkstyle.setToolVersion("10.22.0"); + checkstyle.setToolVersion("10.23.0"); checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle")); String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion(); DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies(); diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 29ea6de76cf..6e3e7bc78b5 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -20,7 +20,7 @@ dependencies { api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.1")) api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.8.0")) api(platform("org.junit:junit-bom:5.12.1")) - api(platform("org.mockito:mockito-bom:5.16.1")) + api(platform("org.mockito:mockito-bom:5.17.0")) constraints { api("com.fasterxml:aalto-xml:1.3.2") 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 c578ceaec24..7a23ac75190 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 @@ -313,7 +313,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)) { @@ -429,6 +429,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()); }