Browse Source

Mark bootstrap thread for entire finishBeanFactoryInitialization phase

Closes gh-35398
pull/35587/head
Juergen Hoeller 2 months ago
parent
commit
80e7ee321e
  1. 13
      spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java
  2. 9
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  3. 3
      spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
  4. 55
      spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

13
spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java

@ -152,6 +152,18 @@ public interface ConfigurableListableBeanFactory @@ -152,6 +152,18 @@ public interface ConfigurableListableBeanFactory
*/
boolean isConfigurationFrozen();
/**
* Mark current thread as main bootstrap thread for singleton instantiation,
* with lenient bootstrap locking applying for background threads.
* <p>Any such marker is to be removed at the end of the managed bootstrap in
* {@link #preInstantiateSingletons()}.
* @since 6.2.12
* @see #setBootstrapExecutor
* @see #preInstantiateSingletons()
*/
default void prepareSingletonBootstrap() {
}
/**
* Ensure that all non-lazy-init singletons are instantiated, also considering
* {@link org.springframework.beans.factory.FactoryBean FactoryBeans}.
@ -159,6 +171,7 @@ public interface ConfigurableListableBeanFactory @@ -159,6 +171,7 @@ public interface ConfigurableListableBeanFactory
* @throws BeansException if one of the singleton beans could not be created.
* Note: This may have left the factory with some beans already initialized!
* Call {@link #destroySingletons()} for full cleanup in this case.
* @see #prepareSingletonBootstrap()
* @see #destroySingletons()
*/
void preInstantiateSingletons() throws BeansException;

9
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

@ -1102,6 +1102,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1102,6 +1102,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
@Override
public void prepareSingletonBootstrap() {
this.mainThreadPrefix = getThreadNamePrefix();
}
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
@ -1114,7 +1119,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @@ -1114,7 +1119,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
// Trigger initialization of all non-lazy singleton beans...
this.preInstantiationThread.set(PreInstantiation.MAIN);
this.mainThreadPrefix = getThreadNamePrefix();
if (this.mainThreadPrefix == null) {
this.mainThreadPrefix = getThreadNamePrefix();
}
try {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (String beanName : beanNames) {

3
spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

@ -936,6 +936,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader @@ -936,6 +936,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
@SuppressWarnings("unchecked")
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Mark current thread for singleton instantiation with applied bootstrap locking.
beanFactory.prepareSingletonBootstrap();
// Initialize bootstrap executor for this context.
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {

55
spring-context/src/test/java/org/springframework/context/annotation/BackgroundBootstrapTests.java

@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.core.SpringProperties;
import org.springframework.core.testfixture.EnabledForTestGroups;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@ -68,6 +69,16 @@ class BackgroundBootstrapTests { @@ -68,6 +69,16 @@ class BackgroundBootstrapTests {
ctx.close();
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
void bootstrapWithLoadTimeWeaverAware() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(LoadTimeWeaverAwareBeanConfig.class);
ctx.getBean("testBean1", TestBean.class);
ctx.getBean("testBean2", TestBean.class);
ctx.close();
}
@Test
@Timeout(10)
@EnabledForTestGroups(LONG_RUNNING)
@ -266,6 +277,50 @@ class BackgroundBootstrapTests { @@ -266,6 +277,50 @@ class BackgroundBootstrapTests {
}
@Configuration(proxyBeanMethods = false)
static class LoadTimeWeaverAwareBeanConfig {
@Bean
LoadTimeWeaverAware loadTimeWeaverAware(ObjectProvider<TestBean> testBean1) {
Thread thread = new Thread(testBean1::getObject);
thread.start();
try {
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
return (loadTimeWeaver -> {});
}
@Bean
public TestBean testBean1(TestBean testBean2) {
return new TestBean(testBean2);
}
@Bean @Lazy
public FactoryBean<TestBean> testBean2() {
try {
Thread.sleep(2000);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
TestBean testBean = new TestBean();
return new FactoryBean<>() {
@Override
public TestBean getObject() {
return testBean;
}
@Override
public Class<?> getObjectType() {
return testBean.getClass();
}
};
}
}
@Configuration(proxyBeanMethods = false)
static class StrictLockingBeanConfig {

Loading…
Cancel
Save