37
37
import java .util .Map ;
38
38
import java .util .Optional ;
39
39
import java .util .Set ;
40
+ import java .util .concurrent .CompletableFuture ;
41
+ import java .util .concurrent .CompletionException ;
40
42
import java .util .concurrent .ConcurrentHashMap ;
43
+ import java .util .concurrent .Executor ;
41
44
import java .util .function .Consumer ;
42
45
import java .util .function .Predicate ;
43
46
import java .util .function .Supplier ;
69
72
import org .springframework .beans .factory .config .ConfigurableListableBeanFactory ;
70
73
import org .springframework .beans .factory .config .DependencyDescriptor ;
71
74
import org .springframework .beans .factory .config .NamedBeanHolder ;
75
+ import org .springframework .core .NamedThreadLocal ;
72
76
import org .springframework .core .OrderComparator ;
73
77
import org .springframework .core .Ordered ;
74
78
import org .springframework .core .ResolvableType ;
83
87
import org .springframework .util .CollectionUtils ;
84
88
import org .springframework .util .CompositeIterator ;
85
89
import org .springframework .util .ObjectUtils ;
90
+ import org .springframework .util .ReflectionUtils ;
86
91
import org .springframework .util .StringUtils ;
87
92
88
93
/**
@@ -151,6 +156,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
151
156
/** Whether to allow eager class loading even for lazy-init beans. */
152
157
private boolean allowEagerClassLoading = true ;
153
158
159
+ @ Nullable
160
+ private Executor bootstrapExecutor ;
161
+
154
162
/** Optional OrderComparator for dependency Lists and arrays. */
155
163
@ Nullable
156
164
private Comparator <Object > dependencyComparator ;
@@ -189,6 +197,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
189
197
/** Whether bean definition metadata may be cached for all beans. */
190
198
private volatile boolean configurationFrozen ;
191
199
200
+ private final NamedThreadLocal <PreInstantiation > preInstantiationThread =
201
+ new NamedThreadLocal <>("Pre-instantiation thread marker" );
202
+
192
203
193
204
/**
194
205
* Create a new DefaultListableBeanFactory.
@@ -273,6 +284,17 @@ public boolean isAllowEagerClassLoading() {
273
284
return this .allowEagerClassLoading ;
274
285
}
275
286
287
+ @ Override
288
+ public void setBootstrapExecutor (@ Nullable Executor bootstrapExecutor ) {
289
+ this .bootstrapExecutor = bootstrapExecutor ;
290
+ }
291
+
292
+ @ Override
293
+ @ Nullable
294
+ public Executor getBootstrapExecutor () {
295
+ return this .bootstrapExecutor ;
296
+ }
297
+
276
298
/**
277
299
* Set a {@link java.util.Comparator} for dependency Lists and arrays.
278
300
* @since 4.0
@@ -319,6 +341,7 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
319
341
if (otherFactory instanceof DefaultListableBeanFactory otherListableFactory ) {
320
342
this .allowBeanDefinitionOverriding = otherListableFactory .allowBeanDefinitionOverriding ;
321
343
this .allowEagerClassLoading = otherListableFactory .allowEagerClassLoading ;
344
+ this .bootstrapExecutor = otherListableFactory .bootstrapExecutor ;
322
345
this .dependencyComparator = otherListableFactory .dependencyComparator ;
323
346
// A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware
324
347
setAutowireCandidateResolver (otherListableFactory .getAutowireCandidateResolver ().cloneIfNecessary ());
@@ -954,6 +977,32 @@ protected Object obtainInstanceFromSupplier(Supplier<?> supplier, String beanNam
954
977
return super .obtainInstanceFromSupplier (supplier , beanName , mbd );
955
978
}
956
979
980
+ @ Override
981
+ protected void checkMergedBeanDefinition (RootBeanDefinition mbd , String beanName , @ Nullable Object [] args ) {
982
+ super .checkMergedBeanDefinition (mbd , beanName , args );
983
+
984
+ if (mbd .isBackgroundInit ()) {
985
+ if (this .preInstantiationThread .get () == PreInstantiation .MAIN && getBootstrapExecutor () != null ) {
986
+ throw new BeanCurrentlyInCreationException (beanName , "Bean marked for background " +
987
+ "initialization but requested in mainline thread - declare ObjectProvider " +
988
+ "or lazy injection point in dependent mainline beans" );
989
+ }
990
+ }
991
+ else {
992
+ // Bean intended to be initialized in main bootstrap thread
993
+ if (this .preInstantiationThread .get () == PreInstantiation .BACKGROUND ) {
994
+ throw new BeanCurrentlyInCreationException (beanName , "Bean marked for mainline initialization " +
995
+ "but requested in background thread - enforce early instantiation in mainline thread " +
996
+ "through depends-on '" + beanName + "' declaration for dependent background beans" );
997
+ }
998
+ }
999
+ }
1000
+
1001
+ @ Override
1002
+ protected boolean isCurrentThreadAllowedToHoldSingletonLock () {
1003
+ return (this .preInstantiationThread .get () != PreInstantiation .BACKGROUND );
1004
+ }
1005
+
957
1006
@ Override
958
1007
public void preInstantiateSingletons () throws BeansException {
959
1008
if (logger .isTraceEnabled ()) {
@@ -965,24 +1014,34 @@ public void preInstantiateSingletons() throws BeansException {
965
1014
List <String > beanNames = new ArrayList <>(this .beanDefinitionNames );
966
1015
967
1016
// Trigger initialization of all non-lazy singleton beans...
968
- for (String beanName : beanNames ) {
969
- RootBeanDefinition bd = getMergedLocalBeanDefinition (beanName );
970
- if (!bd .isAbstract () && bd .isSingleton () && !bd .isLazyInit ()) {
971
- if (isFactoryBean (beanName )) {
972
- Object bean = getBean (FACTORY_BEAN_PREFIX + beanName );
973
- if (bean instanceof SmartFactoryBean <?> smartFactoryBean && smartFactoryBean .isEagerInit ()) {
974
- getBean (beanName );
1017
+ List <CompletableFuture <?>> futures = new ArrayList <>();
1018
+ this .preInstantiationThread .set (PreInstantiation .MAIN );
1019
+ try {
1020
+ for (String beanName : beanNames ) {
1021
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition (beanName );
1022
+ if (!mbd .isAbstract () && mbd .isSingleton ()) {
1023
+ CompletableFuture <?> future = preInstantiateSingleton (beanName , mbd );
1024
+ if (future != null ) {
1025
+ futures .add (future );
975
1026
}
976
1027
}
977
- else {
978
- getBean (beanName );
979
- }
1028
+ }
1029
+ }
1030
+ finally {
1031
+ this .preInstantiationThread .set (null );
1032
+ }
1033
+ if (!futures .isEmpty ()) {
1034
+ try {
1035
+ CompletableFuture .allOf (futures .toArray (new CompletableFuture <?>[0 ])).join ();
1036
+ }
1037
+ catch (CompletionException ex ) {
1038
+ ReflectionUtils .rethrowRuntimeException (ex .getCause ());
980
1039
}
981
1040
}
982
1041
983
1042
// Trigger post-initialization callback for all applicable beans...
984
1043
for (String beanName : beanNames ) {
985
- Object singletonInstance = getSingleton (beanName );
1044
+ Object singletonInstance = getSingleton (beanName , false );
986
1045
if (singletonInstance instanceof SmartInitializingSingleton smartSingleton ) {
987
1046
StartupStep smartInitialize = getApplicationStartup ().start ("spring.beans.smart-initialize" )
988
1047
.tag ("beanName" , beanName );
@@ -992,6 +1051,69 @@ public void preInstantiateSingletons() throws BeansException {
992
1051
}
993
1052
}
994
1053
1054
+ @ Nullable
1055
+ private CompletableFuture <?> preInstantiateSingleton (String beanName , RootBeanDefinition mbd ) {
1056
+ if (mbd .isBackgroundInit ()) {
1057
+ Executor executor = getBootstrapExecutor ();
1058
+ if (executor != null ) {
1059
+ String [] dependsOn = mbd .getDependsOn ();
1060
+ if (dependsOn != null ) {
1061
+ for (String dep : dependsOn ) {
1062
+ getBean (dep );
1063
+ }
1064
+ }
1065
+ CompletableFuture <?> future = CompletableFuture .runAsync (
1066
+ () -> instantiateSingletonInBackgroundThread (beanName ), executor );
1067
+ addSingletonFactory (beanName , () -> {
1068
+ try {
1069
+ future .join ();
1070
+ }
1071
+ catch (CompletionException ex ) {
1072
+ ReflectionUtils .rethrowRuntimeException (ex .getCause ());
1073
+ }
1074
+ return future ; // not to be exposed, just to lead to ClassCastException in case of mismatch
1075
+ });
1076
+ return (!mbd .isLazyInit () ? future : null );
1077
+ }
1078
+ else if (logger .isInfoEnabled ()) {
1079
+ logger .info ("Bean '" + beanName + "' marked for background initialization " +
1080
+ "without bootstrap executor configured - falling back to mainline initialization" );
1081
+ }
1082
+ }
1083
+ if (!mbd .isLazyInit ()) {
1084
+ instantiateSingleton (beanName );
1085
+ }
1086
+ return null ;
1087
+ }
1088
+
1089
+ private void instantiateSingletonInBackgroundThread (String beanName ) {
1090
+ this .preInstantiationThread .set (PreInstantiation .BACKGROUND );
1091
+ try {
1092
+ instantiateSingleton (beanName );
1093
+ }
1094
+ catch (RuntimeException | Error ex ) {
1095
+ if (logger .isWarnEnabled ()) {
1096
+ logger .warn ("Failed to instantiate singleton bean '" + beanName + "' in background thread" , ex );
1097
+ }
1098
+ throw ex ;
1099
+ }
1100
+ finally {
1101
+ this .preInstantiationThread .set (null );
1102
+ }
1103
+ }
1104
+
1105
+ private void instantiateSingleton (String beanName ) {
1106
+ if (isFactoryBean (beanName )) {
1107
+ Object bean = getBean (FACTORY_BEAN_PREFIX + beanName );
1108
+ if (bean instanceof SmartFactoryBean <?> smartFactoryBean && smartFactoryBean .isEagerInit ()) {
1109
+ getBean (beanName );
1110
+ }
1111
+ }
1112
+ else {
1113
+ getBean (beanName );
1114
+ }
1115
+ }
1116
+
995
1117
996
1118
//---------------------------------------------------------------------
997
1119
// Implementation of BeanDefinitionRegistry interface
@@ -2395,4 +2517,10 @@ public Object getOrderSource(Object obj) {
2395
2517
}
2396
2518
}
2397
2519
2520
+
2521
+ private enum PreInstantiation {
2522
+
2523
+ MAIN , BACKGROUND ;
2524
+ }
2525
+
2398
2526
}
0 commit comments