Introduce background bootstrapping for individual singleton beans

Closes gh-13410
Closes gh-19487
See gh-23501
This commit is contained in:
Juergen Hoeller
2024-02-27 22:33:18 +01:00
parent 9e7c64270b
commit 17b2087198
11 changed files with 410 additions and 64 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.context;
import java.io.Closeable;
import java.util.concurrent.Executor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
@@ -53,6 +54,16 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life
*/
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
* The name of the {@link Executor bootstrap executor} bean in the context.
* If none is supplied, no background bootstrapping will be active.
* @since 6.2
* @see java.util.concurrent.Executor
* @see org.springframework.core.task.TaskExecutor
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setBootstrapExecutor
*/
String BOOTSTRAP_EXECUTOR_BEAN_NAME = "bootstrapExecutor";
/**
* Name of the ConversionService bean in the factory.
* If none is supplied, default conversion rules apply.

View File

@@ -260,6 +260,20 @@ public @interface Bean {
*/
boolean defaultCandidate() default true;
/**
* The bootstrap mode for this bean: default is the main pre-instantiation thread
* for non-lazy singleton beans and the caller thread for prototype beans.
* <p>Set {@link Bootstrap#BACKGROUND} to allow for instantiating this bean on a
* background thread. For a non-lazy singleton, a background pre-instantiation
* thread can be used then, while still enforcing the completion at the end of
* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}.
* For a lazy singleton, a background pre-instantiation thread can be used as well
* - with completion allowed at a later point, enforcing it when actually accessed.
* @since 6.2
* @see Lazy
*/
Bootstrap bootstrap() default Bootstrap.DEFAULT;
/**
* The optional name of a method to call on the bean instance during initialization.
* Not commonly used, given that the method may be called programmatically directly
@@ -299,4 +313,28 @@ public @interface Bean {
*/
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
/**
* Local enumeration for the bootstrap mode.
* @since 6.2
* @see #bootstrap()
*/
enum Bootstrap {
/**
* Constant to indicate the main pre-instantiation thread for non-lazy
* singleton beans and the caller thread for prototype beans.
*/
DEFAULT,
/**
* Allow for instantiating a bean on a background thread.
* <p>For a non-lazy singleton, a background pre-instantiation thread
* can be used while still enforcing the completion on context refresh.
* For a lazy singleton, a background pre-instantiation thread can be used
* with completion allowed at a later point (when actually accessed).
*/
BACKGROUND,
}
}

View File

@@ -251,6 +251,11 @@ class ConfigurationClassBeanDefinitionReader {
beanDef.setDefaultCandidate(false);
}
Bean.Bootstrap instantiation = bean.getEnum("bootstrap");
if (instantiation == Bean.Bootstrap.BACKGROUND) {
beanDef.setBackgroundInit(true);
}
String initMethodName = bean.getString("initMethod");
if (StringUtils.hasText(initMethodName)) {
beanDef.setInitMethodName(initMethodName);

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -137,17 +138,6 @@ import org.springframework.util.ReflectionUtils;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* The name of the {@link MessageSource} bean in the context.
* If none is supplied, message resolution is delegated to the parent.
@@ -168,6 +158,17 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
@@ -806,8 +807,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the MessageSource.
* Use parent's if none defined in this context.
* Initialize the {@link MessageSource}.
* <p>Uses parent's {@code MessageSource} if none defined in this context.
* @see #MESSAGE_SOURCE_BEAN_NAME
*/
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
@@ -837,8 +839,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* Initialize the {@link ApplicationEventMulticaster}.
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
@@ -861,15 +864,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the LifecycleProcessor.
* Uses DefaultLifecycleProcessor if none defined in the context.
* Initialize the {@link LifecycleProcessor}.
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
* @since 3.0
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isTraceEnabled()) {
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
@@ -929,6 +933,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize bootstrap executor for this context.
if (beanFactory.containsBean(BOOTSTRAP_EXECUTOR_BEAN_NAME) &&
beanFactory.isTypeMatch(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class)) {
beanFactory.setBootstrapExecutor(
beanFactory.getBean(BOOTSTRAP_EXECUTOR_BEAN_NAME, Executor.class));
}
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {