16
16
17
17
package org .springframework .test .context .support ;
18
18
19
- import java .lang .reflect .InvocationTargetException ;
20
19
import java .util .ArrayList ;
21
20
import java .util .Arrays ;
22
21
import java .util .Collection ;
32
31
import org .springframework .beans .BeanInstantiationException ;
33
32
import org .springframework .beans .BeanUtils ;
34
33
import org .springframework .core .annotation .AnnotationAwareOrderComparator ;
35
- import org .springframework .core .io .support .SpringFactoriesLoader ;
36
- import org .springframework .core .io .support .SpringFactoriesLoader .FailureHandler ;
37
34
import org .springframework .lang .Nullable ;
38
35
import org .springframework .test .context .BootstrapContext ;
39
36
import org .springframework .test .context .CacheAwareContextLoaderDelegate ;
52
49
import org .springframework .test .context .TestExecutionListener ;
53
50
import org .springframework .test .context .TestExecutionListeners ;
54
51
import org .springframework .test .context .TestExecutionListeners .MergeMode ;
52
+ import org .springframework .test .context .util .TestContextSpringFactoriesUtils ;
55
53
import org .springframework .util .Assert ;
56
54
import org .springframework .util .ClassUtils ;
57
55
import org .springframework .util .StringUtils ;
@@ -189,30 +187,15 @@ else if (logger.isDebugEnabled()) {
189
187
/**
190
188
* Get the default {@link TestExecutionListener TestExecutionListeners} for
191
189
* this bootstrapper.
190
+ * <p>The default implementation delegates to
191
+ * {@link TestContextSpringFactoriesUtils#loadFactoryImplementations(Class)}.
192
192
* <p>This method is invoked by {@link #getTestExecutionListeners()}.
193
- * <p>The default implementation looks up and instantiates all
194
- * {@code org.springframework.test.context.TestExecutionListener} entries
195
- * configured in all {@code META-INF/spring.factories} files on the classpath.
196
- * <p>If a particular listener cannot be loaded due to a {@link LinkageError}
197
- * or {@link ClassNotFoundException}, a {@code DEBUG} message will be logged,
198
- * but the associated exception will not be rethrown. A {@link RuntimeException}
199
- * or any other {@link Error} will be rethrown. Any other exception will be
200
- * thrown wrapped in an {@link IllegalStateException}.
201
193
* @return an <em>unmodifiable</em> list of default {@code TestExecutionListener}
202
194
* instances
203
195
* @since 6.0
204
- * @see SpringFactoriesLoader#forDefaultResourceLocation()
205
- * @see SpringFactoriesLoader#load(Class, FailureHandler)
206
196
*/
207
197
protected List <TestExecutionListener > getDefaultTestExecutionListeners () {
208
- SpringFactoriesLoader loader = SpringFactoriesLoader .forDefaultResourceLocation (getClass ().getClassLoader ());
209
- List <TestExecutionListener > listeners =
210
- loader .load (TestExecutionListener .class , this ::handleInstantiationFailure );
211
- if (logger .isTraceEnabled ()) {
212
- logger .trace ("Loaded default TestExecutionListener implementations from location [%s]: %s"
213
- .formatted (SpringFactoriesLoader .FACTORIES_RESOURCE_LOCATION , classNames (listeners )));
214
- }
215
- return Collections .unmodifiableList (listeners );
198
+ return TestContextSpringFactoriesUtils .loadFactoryImplementations (TestExecutionListener .class );
216
199
}
217
200
218
201
@ SuppressWarnings ("unchecked" )
@@ -225,7 +208,14 @@ private List<TestExecutionListener> instantiateListeners(Class<? extends TestExe
225
208
catch (BeanInstantiationException ex ) {
226
209
Throwable cause = ex .getCause ();
227
210
if (cause instanceof ClassNotFoundException || cause instanceof NoClassDefFoundError ) {
228
- logSkippedComponent (TestExecutionListener .class , listenerClass .getName (), cause );
211
+ if (logger .isDebugEnabled ()) {
212
+ logger .debug ("""
213
+ Skipping candidate %1$s [%2$s] due to a missing dependency. \
214
+ Specify custom %1$s classes or make the default %1$s classes \
215
+ and their required dependencies available. Offending class: [%3$s]"""
216
+ .formatted (TestExecutionListener .class .getSimpleName (), listenerClass .getName (),
217
+ cause .getMessage ()));
218
+ }
229
219
}
230
220
else {
231
221
throw ex ;
@@ -409,21 +399,12 @@ else if (logger.isDebugEnabled()) {
409
399
410
400
/**
411
401
* Get the {@link ContextCustomizerFactory} instances for this bootstrapper.
412
- * <p>The default implementation uses the {@link SpringFactoriesLoader} mechanism
413
- * for loading factories configured in all {@code META-INF/spring.factories}
414
- * files on the classpath.
402
+ * <p>The default implementation delegates to
403
+ * {@link TestContextSpringFactoriesUtils#loadFactoryImplementations(Class)}.
415
404
* @since 4.3
416
- * @see SpringFactoriesLoader#loadFactories
417
405
*/
418
406
protected List <ContextCustomizerFactory > getContextCustomizerFactories () {
419
- SpringFactoriesLoader loader = SpringFactoriesLoader .forDefaultResourceLocation (getClass ().getClassLoader ());
420
- List <ContextCustomizerFactory > factories =
421
- loader .load (ContextCustomizerFactory .class , this ::handleInstantiationFailure );
422
- if (logger .isTraceEnabled ()) {
423
- logger .trace ("Loaded ContextCustomizerFactory implementations from location [%s]: %s"
424
- .formatted (SpringFactoriesLoader .FACTORIES_RESOURCE_LOCATION , classNames (factories )));
425
- }
426
- return factories ;
407
+ return TestContextSpringFactoriesUtils .loadFactoryImplementations (ContextCustomizerFactory .class );
427
408
}
428
409
429
410
/**
@@ -552,53 +533,10 @@ protected MergedContextConfiguration processMergedContextConfiguration(MergedCon
552
533
}
553
534
554
535
555
- private void handleInstantiationFailure (
556
- Class <?> factoryType , String factoryImplementationName , Throwable failure ) {
557
-
558
- Throwable ex = (failure instanceof InvocationTargetException ite ?
559
- ite .getTargetException () : failure );
560
- if (ex instanceof ClassNotFoundException || ex instanceof NoClassDefFoundError ) {
561
- logSkippedComponent (factoryType , factoryImplementationName , ex );
562
- }
563
- else if (ex instanceof LinkageError ) {
564
- if (logger .isDebugEnabled ()) {
565
- logger .debug ("""
566
- Could not load %1$s [%2$s]. Specify custom %1$s classes or make the default %1$s classes \
567
- available.""" .formatted (factoryType .getSimpleName (), factoryImplementationName ), ex );
568
- }
569
- }
570
- else {
571
- if (ex instanceof RuntimeException runtimeException ) {
572
- throw runtimeException ;
573
- }
574
- if (ex instanceof Error error ) {
575
- throw error ;
576
- }
577
- throw new IllegalStateException (
578
- "Failed to load %s [%s]." .formatted (factoryType .getSimpleName (), factoryImplementationName ), ex );
579
- }
580
- }
581
-
582
- private void logSkippedComponent (Class <?> factoryType , String factoryImplementationName , Throwable ex ) {
583
- // TestExecutionListener/ContextCustomizerFactory not applicable due to a missing dependency
584
- if (logger .isDebugEnabled ()) {
585
- logger .debug ("""
586
- Skipping candidate %1$s [%2$s] due to a missing dependency. \
587
- Specify custom %1$s classes or make the default %1$s classes \
588
- and their required dependencies available. Offending class: [%3$s]"""
589
- .formatted (factoryType .getSimpleName (), factoryImplementationName , ex .getMessage ()));
590
- }
591
- }
592
-
593
-
594
536
private static List <String > classSimpleNames (Collection <?> components ) {
595
537
return components .stream ().map (Object ::getClass ).map (Class ::getSimpleName ).toList ();
596
538
}
597
539
598
- private static List <String > classNames (Collection <?> components ) {
599
- return components .stream ().map (Object ::getClass ).map (Class ::getName ).toList ();
600
- }
601
-
602
540
private static boolean areAllEmpty (Collection <?>... collections ) {
603
541
return Arrays .stream (collections ).allMatch (Collection ::isEmpty );
604
542
}
0 commit comments