Avoid lenient locking for additional external bootstrap threads

Includes spring.locking.strict revision to differentiate between true, false, not set.
Includes checkFlag accessor on SpringProperties, also used in StatementCreatorUtils.

Closes gh-34729
See gh-34303
This commit is contained in:
Juergen Hoeller
2025-04-10 18:33:21 +02:00
parent 7f2c1f447f
commit eea6addd26
5 changed files with 163 additions and 23 deletions

View File

@@ -16,6 +16,9 @@
package org.springframework.context.annotation;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
@@ -67,7 +70,7 @@ class BackgroundBootstrapTests {
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithStrictLockingThread() {
void bootstrapWithStrictLockingFlag() {
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME);
try {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(StrictLockingBeanConfig.class);
@@ -79,6 +82,42 @@ class BackgroundBootstrapTests {
}
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithStrictLockingInferred() throws InterruptedException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(InferredLockingBeanConfig.class);
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.submit(() -> ctx.refresh());
Thread.sleep(500);
threadPool.submit(() -> ctx.getBean("testBean2"));
Thread.sleep(1000);
assertThat(ctx.getBean("testBean2", TestBean.class).getSpouse()).isSameAs(ctx.getBean("testBean1"));
ctx.close();
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithStrictLockingTurnedOff() throws InterruptedException {
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, false);
try {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(InferredLockingBeanConfig.class);
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.submit(() -> ctx.refresh());
Thread.sleep(500);
threadPool.submit(() -> ctx.getBean("testBean2"));
Thread.sleep(1000);
assertThat(ctx.getBean("testBean2", TestBean.class).getSpouse()).isNull();
ctx.close();
}
finally {
SpringProperties.setProperty(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, null);
}
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
@@ -128,6 +167,24 @@ class BackgroundBootstrapTests {
ctx.close();
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithCustomExecutorAndStrictLocking() {
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME);
try {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.getBean("testBean3", TestBean.class);
ctx.getBean("testBean4", TestBean.class);
ctx.close();
}
finally {
SpringProperties.setProperty(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME, null);
}
}
@Configuration(proxyBeanMethods = false)
static class UnmanagedThreadBeanConfig {
@@ -220,6 +277,27 @@ class BackgroundBootstrapTests {
}
@Configuration(proxyBeanMethods = false)
static class InferredLockingBeanConfig {
@Bean
public TestBean testBean1() {
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
return new TestBean("testBean1");
}
@Bean
public TestBean testBean2(ConfigurableListableBeanFactory beanFactory) {
return new TestBean((TestBean) beanFactory.getSingleton("testBean1"));
}
}
@Configuration(proxyBeanMethods = false)
static class CircularReferenceAgainstMainThreadBeanConfig {
@@ -377,13 +455,13 @@ class BackgroundBootstrapTests {
@Bean(bootstrap = BACKGROUND) @DependsOn("testBean3")
public TestBean testBean1(TestBean testBean3) throws InterruptedException {
Thread.sleep(3000);
Thread.sleep(6000);
return new TestBean();
}
@Bean(bootstrap = BACKGROUND) @Lazy
public TestBean testBean2() throws InterruptedException {
Thread.sleep(3000);
Thread.sleep(6000);
return new TestBean();
}