44
44
45
45
/**
46
46
* A {@link BeanFactoryPostProcessor} implementation that processes identified
47
- * use of {@link BeanOverride} and adapts the {@link BeanFactory} accordingly.
47
+ * use of {@link BeanOverride @BeanOverride} and adapts the {@link BeanFactory}
48
+ * accordingly.
48
49
*
49
50
* <p>For each override, the bean factory is prepared according to the chosen
50
- * {@link BeanOverrideStrategy overriding strategy}. The override value is created,
51
+ * {@linkplain BeanOverrideStrategy override strategy}. The override value is created,
51
52
* if necessary, and the necessary infrastructure is updated to allow the value
52
53
* to be injected in the corresponding {@linkplain OverrideMetadata#getField() field}
53
54
* of the test class.
54
55
*
55
- * <p>This processor does not work against a particular test class, it only prepares
56
- * the bean factory for the identified, unique set of bean overrides.
56
+ * <p>This processor does not work against a particular test class, but rather
57
+ * only prepares the bean factory for the identified, unique set of bean overrides.
57
58
*
58
59
* @author Simon Baslé
59
60
* @author Stephane Nicoll
61
+ * @author Sam Brannen
60
62
* @since 6.2
61
63
*/
62
64
class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor , Ordered {
@@ -69,11 +71,11 @@ class BeanOverrideBeanFactoryPostProcessor implements BeanFactoryPostProcessor,
69
71
70
72
71
73
/**
72
- * Create a new {@code BeanOverrideBeanFactoryPostProcessor} instance with
73
- * the set of {@link OverrideMetadata} to process, using the given
74
+ * Create a new {@code BeanOverrideBeanFactoryPostProcessor} with the supplied
75
+ * set of {@link OverrideMetadata} to process, using the given
74
76
* {@link BeanOverrideRegistrar}.
75
77
* @param metadata the {@link OverrideMetadata} instances to process
76
- * @param overrideRegistrar the {@link BeanOverrideRegistrar} used to track
78
+ * @param overrideRegistrar the {@code BeanOverrideRegistrar} used to track
77
79
* metadata
78
80
*/
79
81
public BeanOverrideBeanFactoryPostProcessor (Set <OverrideMetadata > metadata ,
@@ -84,21 +86,18 @@ public BeanOverrideBeanFactoryPostProcessor(Set<OverrideMetadata> metadata,
84
86
}
85
87
86
88
89
+ @ Override
90
+ public int getOrder () {
91
+ return Ordered .LOWEST_PRECEDENCE - 10 ;
92
+ }
93
+
87
94
@ Override
88
95
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory ) throws BeansException {
89
96
if (!(beanFactory instanceof BeanDefinitionRegistry registry )) {
90
97
throw new IllegalStateException ("Cannot process bean override with a BeanFactory " +
91
98
"that doesn't implement BeanDefinitionRegistry: " + beanFactory .getClass ());
92
99
}
93
- postProcessWithRegistry (beanFactory , registry );
94
- }
95
100
96
- @ Override
97
- public int getOrder () {
98
- return Ordered .LOWEST_PRECEDENCE - 10 ;
99
- }
100
-
101
- private void postProcessWithRegistry (ConfigurableListableBeanFactory beanFactory , BeanDefinitionRegistry registry ) {
102
101
for (OverrideMetadata metadata : this .metadata ) {
103
102
registerBeanOverride (beanFactory , registry , metadata );
104
103
}
@@ -108,15 +107,13 @@ private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, B
108
107
OverrideMetadata overrideMetadata ) {
109
108
110
109
switch (overrideMetadata .getStrategy ()) {
111
- case REPLACE_DEFINITION ->
112
- registerReplaceDefinition (beanFactory , registry , overrideMetadata , true );
113
- case REPLACE_OR_CREATE_DEFINITION ->
114
- registerReplaceDefinition (beanFactory , registry , overrideMetadata , false );
115
- case WRAP_BEAN -> registerWrapBean (beanFactory , overrideMetadata );
110
+ case REPLACE_DEFINITION -> replaceDefinition (beanFactory , registry , overrideMetadata , true );
111
+ case REPLACE_OR_CREATE_DEFINITION -> replaceDefinition (beanFactory , registry , overrideMetadata , false );
112
+ case WRAP_BEAN -> wrapBean (beanFactory , overrideMetadata );
116
113
}
117
114
}
118
115
119
- private void registerReplaceDefinition (ConfigurableListableBeanFactory beanFactory , BeanDefinitionRegistry registry ,
116
+ private void replaceDefinition (ConfigurableListableBeanFactory beanFactory , BeanDefinitionRegistry registry ,
120
117
OverrideMetadata overrideMetadata , boolean enforceExistingDefinition ) {
121
118
122
119
// The following is a "dummy" bean definition which should not be used to
@@ -169,96 +166,116 @@ else if (enforceExistingDefinition) {
169
166
}
170
167
}
171
168
172
- private String getBeanNameForType (ConfigurableListableBeanFactory beanFactory , BeanDefinitionRegistry registry ,
173
- OverrideMetadata overrideMetadata , RootBeanDefinition beanDefinition , boolean enforceExistingDefinition ) {
174
- Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , overrideMetadata , true );
175
- int candidateCount = candidateNames .size ();
176
- if (candidateCount == 1 ) {
177
- return candidateNames .iterator ().next ();
178
- }
179
- else if (candidateCount == 0 ) {
180
- if (enforceExistingDefinition ) {
181
- Field field = overrideMetadata .getField ();
182
- throw new IllegalStateException (
183
- "Unable to override bean: no bean definitions of type %s (as required by annotated field '%s.%s')"
184
- .formatted (overrideMetadata .getBeanType (), field .getDeclaringClass ().getSimpleName (), field .getName ()));
185
- }
186
- return this .beanNameGenerator .generateBeanName (beanDefinition , registry );
187
- }
188
- Field field = overrideMetadata .getField ();
189
- throw new IllegalStateException (String .format (
190
- "Unable to select a bean definition to override: found %s bean definitions of type %s " +
191
- "(as required by annotated field '%s.%s'): %s" ,
192
- candidateCount , overrideMetadata .getBeanType (), field .getDeclaringClass ().getSimpleName (),
193
- field .getName (), candidateNames ));
194
- }
195
-
196
169
/**
197
170
* Check that the expected bean name is registered and matches the type to override.
198
171
* <p>If so, put the override metadata in the early tracking map.
199
172
* <p>The map will later be checked to see if a given bean should be wrapped
200
173
* upon creation, during the {@link WrapEarlyBeanPostProcessor#getEarlyBeanReference(Object, String)}
201
174
* phase.
202
175
*/
203
- private void registerWrapBean (ConfigurableListableBeanFactory beanFactory , OverrideMetadata metadata ) {
204
- String beanName = metadata .getBeanName ();
176
+ private void wrapBean (ConfigurableListableBeanFactory beanFactory , OverrideMetadata overrideMetadata ) {
177
+ String beanName = overrideMetadata .getBeanName ();
205
178
if (beanName == null ) {
206
- Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , metadata , true );
179
+ Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , overrideMetadata , true );
207
180
int candidateCount = candidateNames .size ();
208
181
if (candidateCount != 1 ) {
209
- Field field = metadata .getField ();
182
+ Field field = overrideMetadata .getField ();
210
183
throw new IllegalStateException ("Unable to select a bean to override by wrapping: found " +
211
- candidateCount + " bean instances of type " + metadata .getBeanType () +
184
+ candidateCount + " bean instances of type " + overrideMetadata .getBeanType () +
212
185
" (as required by annotated field '" + field .getDeclaringClass ().getSimpleName () +
213
186
"." + field .getName () + "')" + (candidateCount > 0 ? ": " + candidateNames : "" ));
214
187
}
215
188
beanName = BeanFactoryUtils .transformedBeanName (candidateNames .iterator ().next ());
216
189
}
217
190
else {
218
- Set <String > candidates = getExistingBeanNamesByType (beanFactory , metadata , false );
191
+ Set <String > candidates = getExistingBeanNamesByType (beanFactory , overrideMetadata , false );
219
192
if (!candidates .contains (beanName )) {
220
- throw new IllegalStateException ("Unable to override bean '" + beanName + "' by wrapping: there is no " +
221
- "existing bean instance with that name of type " + metadata .getBeanType ());
193
+ throw new IllegalStateException ("""
194
+ Unable to override bean by wrapping: there is no existing bean definition \
195
+ with name [%s] and type [%s]."""
196
+ .formatted (beanName , overrideMetadata .getBeanType ()));
197
+ }
198
+ }
199
+ this .overrideRegistrar .markWrapEarly (overrideMetadata , beanName );
200
+ this .overrideRegistrar .registerNameForMetadata (overrideMetadata , beanName );
201
+ }
202
+
203
+ private String getBeanNameForType (ConfigurableListableBeanFactory beanFactory , BeanDefinitionRegistry registry ,
204
+ OverrideMetadata overrideMetadata , RootBeanDefinition beanDefinition , boolean enforceExistingDefinition ) {
205
+
206
+ Set <String > candidateNames = getExistingBeanNamesByType (beanFactory , overrideMetadata , true );
207
+ int candidateCount = candidateNames .size ();
208
+ if (candidateCount == 1 ) {
209
+ return candidateNames .iterator ().next ();
210
+ }
211
+ else if (candidateCount == 0 ) {
212
+ if (enforceExistingDefinition ) {
213
+ Field field = overrideMetadata .getField ();
214
+ throw new IllegalStateException (
215
+ "Unable to override bean: no bean definitions of type %s (as required by annotated field '%s.%s')"
216
+ .formatted (overrideMetadata .getBeanType (), field .getDeclaringClass ().getSimpleName (), field .getName ()));
222
217
}
218
+ return this .beanNameGenerator .generateBeanName (beanDefinition , registry );
223
219
}
224
- this .overrideRegistrar .markWrapEarly (metadata , beanName );
225
- this .overrideRegistrar .registerNameForMetadata (metadata , beanName );
220
+
221
+ Field field = overrideMetadata .getField ();
222
+ throw new IllegalStateException ("""
223
+ Unable to select a bean definition to override: found %s bean definitions of type %s \
224
+ (as required by annotated field '%s.%s'): %s"""
225
+ .formatted (candidateCount , overrideMetadata .getBeanType (), field .getDeclaringClass ().getSimpleName (),
226
+ field .getName (), candidateNames ));
226
227
}
227
228
228
229
private Set <String > getExistingBeanNamesByType (ConfigurableListableBeanFactory beanFactory , OverrideMetadata metadata ,
229
230
boolean checkAutowiredCandidate ) {
230
231
231
232
ResolvableType resolvableType = metadata .getBeanType ();
232
- Set <String > beans = new LinkedHashSet <>(
233
- Arrays .asList (beanFactory .getBeanNamesForType (resolvableType , true , false )));
234
233
Class <?> type = resolvableType .resolve (Object .class );
234
+
235
+ // Start with matching bean names for type, excluding FactoryBeans.
236
+ Set <String > beanNames = new LinkedHashSet <>(
237
+ Arrays .asList (beanFactory .getBeanNamesForType (resolvableType , true , false )));
238
+
239
+ // Add matching FactoryBeans as well.
235
240
for (String beanName : beanFactory .getBeanNamesForType (FactoryBean .class , true , false )) {
236
241
beanName = BeanFactoryUtils .transformedBeanName (beanName );
237
242
BeanDefinition beanDefinition = beanFactory .getBeanDefinition (beanName );
238
243
Object attribute = beanDefinition .getAttribute (FactoryBean .OBJECT_TYPE_ATTRIBUTE );
239
244
if (resolvableType .equals (attribute ) || type .equals (attribute )) {
240
- beans .add (beanName );
245
+ beanNames .add (beanName );
241
246
}
242
247
}
248
+
249
+ // Filter out non-matching autowire candidates.
243
250
if (checkAutowiredCandidate ) {
244
251
DependencyDescriptor descriptor = new DependencyDescriptor (metadata .getField (), true );
245
- beans .removeIf (beanName -> ScopedProxyUtils .isScopedTarget (beanName ) ||
246
- !beanFactory .isAutowireCandidate (beanName , descriptor ));
252
+ beanNames .removeIf (beanName -> !beanFactory .isAutowireCandidate (beanName , descriptor ));
247
253
}
248
- else {
249
- beans .removeIf (ScopedProxyUtils ::isScopedTarget );
250
- }
251
- // In case of multiple matches, last resort fallback on the field's name
252
- if (beans .size () > 1 ) {
254
+ // Filter out scoped proxy targets.
255
+ beanNames .removeIf (ScopedProxyUtils ::isScopedTarget );
256
+
257
+ // In case of multiple matches, fall back on the field's name as a last resort.
258
+ if (beanNames .size () > 1 ) {
253
259
String fieldName = metadata .getField ().getName ();
254
- if (beans .contains (fieldName )) {
260
+ if (beanNames .contains (fieldName )) {
255
261
return Set .of (fieldName );
256
262
}
257
263
}
258
- return beans ;
264
+ return beanNames ;
259
265
}
260
266
261
-
267
+ /**
268
+ * Create a pseudo-{@link BeanDefinition} for the supplied {@link OverrideMetadata},
269
+ * whose {@linkplain RootBeanDefinition#getTargetType() target type} and
270
+ * {@linkplain RootBeanDefinition#getQualifiedElement() qualified element} are
271
+ * the {@linkplain OverrideMetadata#getBeanType() bean type} and
272
+ * the {@linkplain OverrideMetadata#getField() field} of the {@code OverrideMetadata},
273
+ * respectively.
274
+ * <p>The returned bean definition should <strong>not</strong> be used to create
275
+ * a bean instance but rather only for the purpose of having suitable bean
276
+ * definition metadata available in the {@link BeanFactory} — for example,
277
+ * for autowiring candidate resolution.
278
+ */
262
279
private static RootBeanDefinition createBeanDefinition (OverrideMetadata metadata ) {
263
280
RootBeanDefinition definition = new RootBeanDefinition (metadata .getBeanType ().resolve ());
264
281
definition .setTargetType (metadata .getBeanType ());
@@ -286,12 +303,10 @@ static class WrapEarlyBeanPostProcessor implements SmartInstantiationAwareBeanPo
286
303
287
304
private final BeanOverrideRegistrar overrideRegistrar ;
288
305
289
-
290
306
WrapEarlyBeanPostProcessor (BeanOverrideRegistrar registrar ) {
291
307
this .overrideRegistrar = registrar ;
292
308
}
293
309
294
-
295
310
@ Override
296
311
public int getOrder () {
297
312
return Ordered .HIGHEST_PRECEDENCE ;
0 commit comments