@@ -133,6 +133,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
133
133
* System property that instructs Spring to enforce strict locking during bean creation,
134
134
* rather than the mix of strict and lenient locking that 6.2 applies by default. Setting
135
135
* this flag to "true" restores 6.1.x style locking in the entire pre-instantiation phase.
136
+ * <p>By default, the factory infers strict locking from the encountered thread names:
137
+ * If additional threads have names that match the thread prefix of the main bootstrap thread,
138
+ * they are considered external (multiple external bootstrap threads calling into the factory)
139
+ * and therefore have strict locking applied to them. This inference can be turned off through
140
+ * explicitly setting this flag to "false" rather than leaving it unspecified.
136
141
* @since 6.2.6
137
142
* @see #preInstantiateSingletons()
138
143
*/
@@ -157,8 +162,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
157
162
private static final Map <String , Reference <DefaultListableBeanFactory >> serializableFactories =
158
163
new ConcurrentHashMap <>(8 );
159
164
160
- /** Whether lenient locking is allowed in this factory. */
161
- private final boolean lenientLockingAllowed = !SpringProperties .getFlag (STRICT_LOCKING_PROPERTY_NAME );
165
+ /** Whether strict locking is enforced or relaxed in this factory. */
166
+ @ Nullable
167
+ private final Boolean strictLocking = SpringProperties .checkFlag (STRICT_LOCKING_PROPERTY_NAME );
162
168
163
169
/** Optional id for this factory, for serialization purposes. */
164
170
@ Nullable
@@ -214,6 +220,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
214
220
215
221
private volatile boolean preInstantiationPhase ;
216
222
223
+ @ Nullable
224
+ private volatile String mainThreadPrefix ;
225
+
217
226
private final NamedThreadLocal <PreInstantiation > preInstantiationThread =
218
227
new NamedThreadLocal <>("Pre-instantiation thread marker" );
219
228
@@ -1045,7 +1054,7 @@ protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName
1045
1054
}
1046
1055
}
1047
1056
else {
1048
- // Bean intended to be initialized in main bootstrap thread
1057
+ // Bean intended to be initialized in main bootstrap thread.
1049
1058
if (this .preInstantiationThread .get () == PreInstantiation .BACKGROUND ) {
1050
1059
throw new BeanCurrentlyInCreationException (beanName , "Bean marked for mainline initialization " +
1051
1060
"but requested in background thread - enforce early instantiation in mainline thread " +
@@ -1057,8 +1066,28 @@ protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName
1057
1066
@ Override
1058
1067
@ Nullable
1059
1068
protected Boolean isCurrentThreadAllowedToHoldSingletonLock () {
1060
- return (this .lenientLockingAllowed && this .preInstantiationPhase ?
1061
- this .preInstantiationThread .get () != PreInstantiation .BACKGROUND : null );
1069
+ if (this .preInstantiationPhase ) {
1070
+ // We only differentiate in the preInstantiateSingletons phase.
1071
+ PreInstantiation preInstantiation = this .preInstantiationThread .get ();
1072
+ if (preInstantiation != null ) {
1073
+ // A Spring-managed thread:
1074
+ // MAIN is allowed to lock (true) or even forced to lock (null),
1075
+ // BACKGROUND is never allowed to lock (false).
1076
+ return switch (preInstantiation ) {
1077
+ case MAIN -> (Boolean .TRUE .equals (this .strictLocking ) ? null : true );
1078
+ case BACKGROUND -> false ;
1079
+ };
1080
+ }
1081
+ if (Boolean .FALSE .equals (this .strictLocking ) ||
1082
+ (this .strictLocking == null && !getThreadNamePrefix ().equals (this .mainThreadPrefix ))) {
1083
+ // An unmanaged thread (assumed to be application-internal) with lenient locking,
1084
+ // and not part of the same thread pool that provided the main bootstrap thread
1085
+ // (excluding scenarios where we are hit by multiple external bootstrap threads).
1086
+ return true ;
1087
+ }
1088
+ }
1089
+ // Traditional behavior: forced to always hold a full lock.
1090
+ return null ;
1062
1091
}
1063
1092
1064
1093
@ Override
@@ -1076,6 +1105,7 @@ public void preInstantiateSingletons() throws BeansException {
1076
1105
1077
1106
this .preInstantiationPhase = true ;
1078
1107
this .preInstantiationThread .set (PreInstantiation .MAIN );
1108
+ this .mainThreadPrefix = getThreadNamePrefix ();
1079
1109
try {
1080
1110
for (String beanName : beanNames ) {
1081
1111
RootBeanDefinition mbd = getMergedLocalBeanDefinition (beanName );
@@ -1088,6 +1118,7 @@ public void preInstantiateSingletons() throws BeansException {
1088
1118
}
1089
1119
}
1090
1120
finally {
1121
+ this .mainThreadPrefix = null ;
1091
1122
this .preInstantiationThread .remove ();
1092
1123
this .preInstantiationPhase = false ;
1093
1124
}
@@ -1183,6 +1214,12 @@ private void instantiateSingleton(String beanName) {
1183
1214
}
1184
1215
}
1185
1216
1217
+ private static String getThreadNamePrefix () {
1218
+ String name = Thread .currentThread ().getName ();
1219
+ int numberSeparator = name .lastIndexOf ('-' );
1220
+ return (numberSeparator >= 0 ? name .substring (0 , numberSeparator ) : name );
1221
+ }
1222
+
1186
1223
1187
1224
//---------------------------------------------------------------------
1188
1225
// Implementation of BeanDefinitionRegistry interface
0 commit comments