@@ -250,26 +250,21 @@ private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideH
250
250
if (beanName == null ) {
251
251
// We are wrapping an existing bean by-type.
252
252
Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , handler , true );
253
- int candidateCount = candidateNames . size ( );
254
- if (candidateCount == 1 ) {
255
- beanName = candidateNames . iterator (). next () ;
253
+ String uniqueCandidate = determineUniqueCandidate ( beanFactory , candidateNames , beanType , field );
254
+ if (uniqueCandidate != null ) {
255
+ beanName = uniqueCandidate ;
256
256
}
257
257
else {
258
- String primaryCandidate = determinePrimaryCandidate (beanFactory , candidateNames , beanType .toClass ());
259
- if (primaryCandidate != null ) {
260
- beanName = primaryCandidate ;
258
+ String message = "Unable to select a bean to wrap: " ;
259
+ int candidateCount = candidateNames .size ();
260
+ if (candidateCount == 0 ) {
261
+ message += "there are no beans of type %s%s." .formatted (beanType , requiredByField (field ));
261
262
}
262
263
else {
263
- String message = "Unable to select a bean to wrap: " ;
264
- if (candidateCount == 0 ) {
265
- message += "there are no beans of type %s%s." .formatted (beanType , requiredByField (field ));
266
- }
267
- else {
268
- message += "found %d beans of type %s%s: %s"
269
- .formatted (candidateCount , beanType , requiredByField (field ), candidateNames );
270
- }
271
- throw new IllegalStateException (message );
264
+ message += "found %d beans of type %s%s: %s"
265
+ .formatted (candidateCount , beanType , requiredByField (field ), candidateNames );
272
266
}
267
+ throw new IllegalStateException (message );
273
268
}
274
269
beanName = BeanFactoryUtils .transformedBeanName (beanName );
275
270
}
@@ -287,18 +282,20 @@ private void wrapBean(ConfigurableListableBeanFactory beanFactory, BeanOverrideH
287
282
this .beanOverrideRegistry .registerBeanOverrideHandler (handler , beanName );
288
283
}
289
284
290
- private @ Nullable String getBeanNameForType (ConfigurableListableBeanFactory beanFactory , BeanOverrideHandler handler ,
285
+ private static @ Nullable String getBeanNameForType (ConfigurableListableBeanFactory beanFactory , BeanOverrideHandler handler ,
291
286
boolean requireExistingBean ) {
292
287
293
288
Field field = handler .getField ();
294
289
ResolvableType beanType = handler .getBeanType ();
295
290
296
291
Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , handler , true );
297
- int candidateCount = candidateNames . size ( );
298
- if (candidateCount == 1 ) {
299
- return candidateNames . iterator (). next () ;
292
+ String uniqueCandidate = determineUniqueCandidate ( beanFactory , candidateNames , beanType , field );
293
+ if (uniqueCandidate != null ) {
294
+ return uniqueCandidate ;
300
295
}
301
- else if (candidateCount == 0 ) {
296
+
297
+ int candidateCount = candidateNames .size ();
298
+ if (candidateCount == 0 ) {
302
299
if (requireExistingBean ) {
303
300
throw new IllegalStateException (
304
301
"Unable to override bean: there are no beans of type %s%s."
@@ -307,18 +304,13 @@ else if (candidateCount == 0) {
307
304
return null ;
308
305
}
309
306
310
- String primaryCandidate = determinePrimaryCandidate (beanFactory , candidateNames , beanType .toClass ());
311
- if (primaryCandidate != null ) {
312
- return primaryCandidate ;
313
- }
314
-
315
307
throw new IllegalStateException (
316
308
"Unable to select a bean to override: found %d beans of type %s%s: %s"
317
309
.formatted (candidateCount , beanType , requiredByField (field ), candidateNames ));
318
310
}
319
311
320
- private Set <String > getExistingBeanNamesByType (ConfigurableListableBeanFactory beanFactory , BeanOverrideHandler handler ,
321
- boolean checkAutowiredCandidate ) {
312
+ private static Set <String > getExistingBeanNamesByType (ConfigurableListableBeanFactory beanFactory ,
313
+ BeanOverrideHandler handler , boolean checkAutowiredCandidate ) {
322
314
323
315
Field field = handler .getField ();
324
316
ResolvableType resolvableType = handler .getBeanType ();
@@ -345,24 +337,54 @@ private Set<String> getExistingBeanNamesByType(ConfigurableListableBeanFactory b
345
337
// Filter out scoped proxy targets.
346
338
beanNames .removeIf (ScopedProxyUtils ::isScopedTarget );
347
339
348
- // In case of multiple matches, fall back on the field's name as a last resort.
349
- if (field != null && beanNames .size () > 1 ) {
340
+ return beanNames ;
341
+ }
342
+
343
+ /**
344
+ * Determine the unique candidate in the given set of bean names.
345
+ * <p>Honors both <em>primary</em> and <em>fallback</em> semantics, and
346
+ * otherwise matches against the field name as a <em>fallback qualifier</em>.
347
+ * @return the name of the unique candidate, or {@code null} if none found
348
+ * @since 6.2.3
349
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#determineAutowireCandidate
350
+ */
351
+ private static @ Nullable String determineUniqueCandidate (ConfigurableListableBeanFactory beanFactory ,
352
+ Set <String > candidateNames , ResolvableType beanType , @ Nullable Field field ) {
353
+
354
+ // Step 0: none or only one
355
+ int candidateCount = candidateNames .size ();
356
+ if (candidateCount == 0 ) {
357
+ return null ;
358
+ }
359
+ if (candidateCount == 1 ) {
360
+ return candidateNames .iterator ().next ();
361
+ }
362
+
363
+ // Step 1: check primary candidate
364
+ String primaryCandidate = determinePrimaryCandidate (beanFactory , candidateNames , beanType .toClass ());
365
+ if (primaryCandidate != null ) {
366
+ return primaryCandidate ;
367
+ }
368
+
369
+ // Step 2: use the field name as a fallback qualifier
370
+ if (field != null ) {
350
371
String fieldName = field .getName ();
351
- if (beanNames .contains (fieldName )) {
352
- return Set . of ( fieldName ) ;
372
+ if (candidateNames .contains (fieldName )) {
373
+ return fieldName ;
353
374
}
354
375
}
355
- return beanNames ;
376
+
377
+ return null ;
356
378
}
357
379
358
380
/**
359
381
* Determine the primary candidate in the given set of bean names.
360
382
* <p>Honors both <em>primary</em> and <em>fallback</em> semantics.
361
383
* @return the name of the primary candidate, or {@code null} if none found
362
- * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#determinePrimaryCandidate(Map, Class)
384
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#determinePrimaryCandidate
363
385
*/
364
- private static @ Nullable String determinePrimaryCandidate (
365
- ConfigurableListableBeanFactory beanFactory , Set <String > candidateBeanNames , Class <?> beanType ) {
386
+ private static @ Nullable String determinePrimaryCandidate (ConfigurableListableBeanFactory beanFactory ,
387
+ Set <String > candidateBeanNames , Class <?> beanType ) {
366
388
367
389
if (candidateBeanNames .isEmpty ()) {
368
390
return null ;
0 commit comments