|
|
|
@ -19,6 +19,7 @@ package org.springframework.context.annotation; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.junit.jupiter.api.Timeout; |
|
|
|
import org.junit.jupiter.api.Timeout; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.BeanCreationException; |
|
|
|
import org.springframework.beans.factory.BeanCurrentlyInCreationException; |
|
|
|
import org.springframework.beans.factory.BeanCurrentlyInCreationException; |
|
|
|
import org.springframework.beans.factory.ObjectProvider; |
|
|
|
import org.springframework.beans.factory.ObjectProvider; |
|
|
|
import org.springframework.beans.factory.UnsatisfiedDependencyException; |
|
|
|
import org.springframework.beans.factory.UnsatisfiedDependencyException; |
|
|
|
@ -42,7 +43,7 @@ import static org.springframework.core.testfixture.TestGroup.LONG_RUNNING; |
|
|
|
class BackgroundBootstrapTests { |
|
|
|
class BackgroundBootstrapTests { |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithUnmanagedThread() { |
|
|
|
void bootstrapWithUnmanagedThread() { |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadBeanConfig.class); |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadBeanConfig.class); |
|
|
|
@ -52,7 +53,7 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithUnmanagedThreads() { |
|
|
|
void bootstrapWithUnmanagedThreads() { |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadsBeanConfig.class); |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(UnmanagedThreadsBeanConfig.class); |
|
|
|
@ -64,7 +65,7 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithStrictLockingThread() { |
|
|
|
void bootstrapWithStrictLockingThread() { |
|
|
|
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME); |
|
|
|
SpringProperties.setFlag(DefaultListableBeanFactory.STRICT_LOCKING_PROPERTY_NAME); |
|
|
|
@ -79,17 +80,26 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithCircularReference() { |
|
|
|
void bootstrapWithCircularReferenceAgainstMainThread() { |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceBeanConfig.class); |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CircularReferenceAgainstMainThreadBeanConfig.class); |
|
|
|
ctx.getBean("testBean1", TestBean.class); |
|
|
|
ctx.getBean("testBean1", TestBean.class); |
|
|
|
ctx.getBean("testBean2", TestBean.class); |
|
|
|
ctx.getBean("testBean2", TestBean.class); |
|
|
|
ctx.close(); |
|
|
|
ctx.close(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
|
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
|
|
|
|
void bootstrapWithCircularReferenceWithBlockingMainThread() { |
|
|
|
|
|
|
|
assertThatExceptionOfType(BeanCreationException.class) |
|
|
|
|
|
|
|
.isThrownBy(() -> new AnnotationConfigApplicationContext(CircularReferenceWithBlockingMainThreadBeanConfig.class)) |
|
|
|
|
|
|
|
.withRootCauseInstanceOf(BeanCurrentlyInCreationException.class); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithCircularReferenceInSameThread() { |
|
|
|
void bootstrapWithCircularReferenceInSameThread() { |
|
|
|
assertThatExceptionOfType(UnsatisfiedDependencyException.class) |
|
|
|
assertThatExceptionOfType(UnsatisfiedDependencyException.class) |
|
|
|
@ -98,7 +108,16 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
@Test |
|
|
|
@Timeout(5) |
|
|
|
@Timeout(10) |
|
|
|
|
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
|
|
|
|
void bootstrapWithCircularReferenceInMultipleThreads() { |
|
|
|
|
|
|
|
assertThatExceptionOfType(BeanCreationException.class) |
|
|
|
|
|
|
|
.isThrownBy(() -> new AnnotationConfigApplicationContext(CircularReferenceInMultipleThreadsBeanConfig.class)) |
|
|
|
|
|
|
|
.withRootCauseInstanceOf(BeanCurrentlyInCreationException.class); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
@Timeout(10) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
@EnabledForTestGroups(LONG_RUNNING) |
|
|
|
void bootstrapWithCustomExecutor() { |
|
|
|
void bootstrapWithCustomExecutor() { |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorBeanConfig.class); |
|
|
|
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CustomExecutorBeanConfig.class); |
|
|
|
@ -202,7 +221,7 @@ class BackgroundBootstrapTests { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
static class CircularReferenceBeanConfig { |
|
|
|
static class CircularReferenceAgainstMainThreadBeanConfig { |
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
@Bean |
|
|
|
public TestBean testBean1(ObjectProvider<TestBean> testBean2) { |
|
|
|
public TestBean testBean1(ObjectProvider<TestBean> testBean2) { |
|
|
|
@ -229,6 +248,46 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
|
|
|
static class CircularReferenceWithBlockingMainThreadBeanConfig { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
public TestBean testBean1(ObjectProvider<TestBean> testBean2) { |
|
|
|
|
|
|
|
Thread thread = new Thread(testBean2::getObject); |
|
|
|
|
|
|
|
thread.setUncaughtExceptionHandler((t, ex) -> System.out.println(System.currentTimeMillis() + " " + ex + " " + t)); |
|
|
|
|
|
|
|
thread.start(); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Thread.sleep(1000); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (InterruptedException ex) { |
|
|
|
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return new TestBean(testBean2.getObject()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
public TestBean testBean2(ObjectProvider<TestBean> 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()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
static class CircularReferenceInSameThreadBeanConfig { |
|
|
|
static class CircularReferenceInSameThreadBeanConfig { |
|
|
|
|
|
|
|
|
|
|
|
@ -262,6 +321,46 @@ class BackgroundBootstrapTests { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
|
|
|
|
static class CircularReferenceInMultipleThreadsBeanConfig { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
public TestBean testBean1(ObjectProvider<TestBean> testBean2, ObjectProvider<TestBean> testBean3) { |
|
|
|
|
|
|
|
new Thread(testBean2::getObject).start(); |
|
|
|
|
|
|
|
new Thread(testBean3::getObject).start(); |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Thread.sleep(2000); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (InterruptedException ex) { |
|
|
|
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return new TestBean(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
public TestBean testBean2(ObjectProvider<TestBean> testBean3) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Thread.sleep(1000); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (InterruptedException ex) { |
|
|
|
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return new TestBean(testBean3.getObject()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Bean |
|
|
|
|
|
|
|
public TestBean testBean3(ObjectProvider<TestBean> testBean2) { |
|
|
|
|
|
|
|
try { |
|
|
|
|
|
|
|
Thread.sleep(1000); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (InterruptedException ex) { |
|
|
|
|
|
|
|
throw new RuntimeException(ex); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return new TestBean(testBean2.getObject()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
@Configuration(proxyBeanMethods = false) |
|
|
|
static class CustomExecutorBeanConfig { |
|
|
|
static class CustomExecutorBeanConfig { |
|
|
|
|
|
|
|
|
|
|
|
|