diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCurrentlyInCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCurrentlyInCreationException.java
index 4c984fb12784..5f5fc7b99d30 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCurrentlyInCreationException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCurrentlyInCreationException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 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.
@@ -32,8 +32,8 @@ public class BeanCurrentlyInCreationException extends BeanCreationException {
* @param beanName the name of the bean requested
*/
public BeanCurrentlyInCreationException(String beanName) {
- super(beanName,
- "Requested bean is currently in creation: Is there an unresolvable circular reference?");
+ super(beanName, "Requested bean is currently in creation: "+
+ "Is there an unresolvable circular reference or an asynchronous initialization dependency?");
}
/**
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java
index 737746018bad..1b7c0598ef17 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 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.beans.factory.config;
import java.beans.PropertyEditor;
+import java.util.concurrent.Executor;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
@@ -25,6 +26,7 @@
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.lang.Nullable;
@@ -146,6 +148,22 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single
@Nullable
BeanExpressionResolver getBeanExpressionResolver();
+ /**
+ * Set the {@link Executor} (possibly a {@link org.springframework.core.task.TaskExecutor})
+ * for background bootstrapping.
+ * @since 6.2
+ * @see AbstractBeanDefinition#setBackgroundInit
+ */
+ void setBootstrapExecutor(@Nullable Executor executor);
+
+ /**
+ * Return the {@link Executor} (possibly a {@link org.springframework.core.task.TaskExecutor})
+ * for background bootstrapping, if any.
+ * @since 6.2
+ */
+ @Nullable
+ Executor getBootstrapExecutor();
+
/**
* Specify a {@link ConversionService} to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
index 2add2e4672b5..450098ae7af2 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
@@ -173,6 +173,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private boolean abstractFlag = false;
+ private boolean backgroundInit = false;
+
@Nullable
private Boolean lazyInit;
@@ -280,6 +282,7 @@ protected AbstractBeanDefinition(BeanDefinition original) {
if (originalAbd.hasMethodOverrides()) {
setMethodOverrides(new MethodOverrides(originalAbd.getMethodOverrides()));
}
+ setBackgroundInit(originalAbd.isBackgroundInit());
Boolean lazyInit = originalAbd.getLazyInit();
if (lazyInit != null) {
setLazyInit(lazyInit);
@@ -358,6 +361,7 @@ public void overrideFrom(BeanDefinition other) {
if (otherAbd.hasMethodOverrides()) {
getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
}
+ setBackgroundInit(otherAbd.isBackgroundInit());
Boolean lazyInit = otherAbd.getLazyInit();
if (lazyInit != null) {
setLazyInit(lazyInit);
@@ -572,6 +576,37 @@ public boolean isAbstract() {
return this.abstractFlag;
}
+ /**
+ * Specify the bootstrap mode for this bean: default is {@code false} for using
+ * the main pre-instantiation thread for non-lazy singleton beans and the caller
+ * thread for prototype beans.
+ *
Set this flag to {@code true} 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 DefaultListableBeanFactory#preInstantiateSingletons()}.
+ * 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.
+ *
Note that this flag may be ignored by bean factories not set up for
+ * background bootstrapping, always applying single-threaded bootstrapping
+ * for non-lazy singleton beans.
+ * @since 6.2
+ * @see #setLazyInit
+ * @see DefaultListableBeanFactory#setBootstrapExecutor
+ */
+ public void setBackgroundInit(boolean backgroundInit) {
+ this.backgroundInit = backgroundInit;
+ }
+
+ /**
+ * Return the bootstrap mode for this bean: default is {@code false} for using
+ * the main pre-instantiation thread for non-lazy singleton beans and the caller
+ * thread for prototype beans.
+ * @since 6.2
+ */
+ public boolean isBackgroundInit() {
+ return this.backgroundInit;
+ }
+
/**
* {@inheritDoc}
*
The default is {@code false}.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 6e71eb5cd955..b10cb39ded83 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -1445,11 +1445,8 @@ private void copyRelevantMergedBeanDefinitionCaches(RootBeanDefinition previous,
* @param mbd the merged bean definition to check
* @param beanName the name of the bean
* @param args the arguments for bean creation, if any
- * @throws BeanDefinitionStoreException in case of validation failure
*/
- protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
- throws BeanDefinitionStoreException {
-
+ protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args) {
if (mbd.isAbstract()) {
throw new BeanIsAbstractException(beanName);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 51ee1a84845d..2c8d4dc794de 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -37,7 +37,10 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -69,6 +72,7 @@
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.NamedBeanHolder;
+import org.springframework.core.NamedThreadLocal;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
@@ -83,6 +87,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.util.CompositeIterator;
import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -151,6 +156,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
/** Whether to allow eager class loading even for lazy-init beans. */
private boolean allowEagerClassLoading = true;
+ @Nullable
+ private Executor bootstrapExecutor;
+
/** Optional OrderComparator for dependency Lists and arrays. */
@Nullable
private Comparator