Skip to content

Commit f89b99b

Browse files
committed
Allow FailureAnalizers without ApplicationContext
Update `SpringApplication` so that `FailureAnalyzers` apply even if the `ApplicationContext` was not created. If no `ApplicationContext` is available, only `FailureAnalyzer` instances that do not implement any `Aware` interfaces are considered. Closes gh-23710
1 parent 84f9603 commit f89b99b

File tree

3 files changed

+58
-33
lines changed

3 files changed

+58
-33
lines changed

Diff for: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

+14-7
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,6 @@ public ConfigurableApplicationContext run(String... args) {
312312
stopWatch.start();
313313
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
314314
ConfigurableApplicationContext context = null;
315-
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
316315
configureHeadlessProperty();
317316
SpringApplicationRunListeners listeners = getRunListeners(args);
318317
listeners.starting(bootstrapContext, this.mainApplicationClass);
@@ -323,8 +322,6 @@ public ConfigurableApplicationContext run(String... args) {
323322
Banner printedBanner = printBanner(environment);
324323
context = createApplicationContext();
325324
context.setApplicationStartup(this.applicationStartup);
326-
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
327-
new Class<?>[] { ConfigurableApplicationContext.class }, context);
328325
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
329326
refreshContext(context);
330327
afterRefresh(context, applicationArguments);
@@ -336,15 +333,15 @@ public ConfigurableApplicationContext run(String... args) {
336333
callRunners(context, applicationArguments);
337334
}
338335
catch (Throwable ex) {
339-
handleRunFailure(context, ex, exceptionReporters, listeners);
336+
handleRunFailure(context, ex, listeners);
340337
throw new IllegalStateException(ex);
341338
}
342339

343340
try {
344341
listeners.running(context);
345342
}
346343
catch (Throwable ex) {
347-
handleRunFailure(context, ex, exceptionReporters, null);
344+
handleRunFailure(context, ex, null);
348345
throw new IllegalStateException(ex);
349346
}
350347
return context;
@@ -812,7 +809,7 @@ private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
812809
}
813810

814811
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
815-
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
812+
SpringApplicationRunListeners listeners) {
816813
try {
817814
try {
818815
handleExitCode(context, exception);
@@ -821,7 +818,7 @@ private void handleRunFailure(ConfigurableApplicationContext context, Throwable
821818
}
822819
}
823820
finally {
824-
reportFailure(exceptionReporters, exception);
821+
reportFailure(getExceptionReporters(context), exception);
825822
if (context != null) {
826823
context.close();
827824
}
@@ -833,6 +830,16 @@ private void handleRunFailure(ConfigurableApplicationContext context, Throwable
833830
ReflectionUtils.rethrowRuntimeException(exception);
834831
}
835832

833+
private Collection<SpringBootExceptionReporter> getExceptionReporters(ConfigurableApplicationContext context) {
834+
try {
835+
return getSpringFactoriesInstances(SpringBootExceptionReporter.class,
836+
new Class<?>[] { ConfigurableApplicationContext.class }, context);
837+
}
838+
catch (Throwable ex) {
839+
return Collections.emptyList();
840+
}
841+
}
842+
836843
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
837844
try {
838845
for (SpringBootExceptionReporter reporter : exceptionReporters) {

Diff for: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/FailureAnalyzers.java

+32-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@
3131
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3232
import org.springframework.core.io.support.SpringFactoriesLoader;
3333
import org.springframework.core.log.LogMessage;
34-
import org.springframework.util.Assert;
3534
import org.springframework.util.ClassUtils;
3635
import org.springframework.util.ReflectionUtils;
3736

@@ -61,42 +60,50 @@ final class FailureAnalyzers implements SpringBootExceptionReporter {
6160
}
6261

6362
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
64-
Assert.notNull(context, "Context must not be null");
65-
this.classLoader = (classLoader != null) ? classLoader : context.getClassLoader();
66-
this.analyzers = loadFailureAnalyzers(this.classLoader);
67-
prepareFailureAnalyzers(this.analyzers, context);
63+
this.classLoader = (classLoader != null) ? classLoader : getClassLoader(context);
64+
this.analyzers = loadFailureAnalyzers(context, this.classLoader);
6865
}
6966

70-
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
71-
List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);
67+
private ClassLoader getClassLoader(ConfigurableApplicationContext context) {
68+
return (context != null) ? context.getClassLoader() : null;
69+
}
70+
71+
private List<FailureAnalyzer> loadFailureAnalyzers(ConfigurableApplicationContext context,
72+
ClassLoader classLoader) {
73+
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);
7274
List<FailureAnalyzer> analyzers = new ArrayList<>();
73-
for (String analyzerName : analyzerNames) {
75+
for (String className : classNames) {
7476
try {
75-
Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor();
76-
ReflectionUtils.makeAccessible(constructor);
77-
analyzers.add((FailureAnalyzer) constructor.newInstance());
77+
FailureAnalyzer analyzer = createAnalyzer(context, className);
78+
if (analyzer != null) {
79+
analyzers.add(analyzer);
80+
}
7881
}
7982
catch (Throwable ex) {
80-
logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex);
83+
logger.trace(LogMessage.format("Failed to load %s", className), ex);
8184
}
8285
}
8386
AnnotationAwareOrderComparator.sort(analyzers);
8487
return analyzers;
8588
}
8689

87-
private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) {
88-
for (FailureAnalyzer analyzer : analyzers) {
89-
prepareAnalyzer(context, analyzer);
90-
}
91-
}
92-
93-
private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) {
94-
if (analyzer instanceof BeanFactoryAware) {
95-
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
96-
}
97-
if (analyzer instanceof EnvironmentAware) {
98-
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
90+
private FailureAnalyzer createAnalyzer(ConfigurableApplicationContext context, String className) throws Exception {
91+
Constructor<?> constructor = ClassUtils.forName(className, this.classLoader).getDeclaredConstructor();
92+
ReflectionUtils.makeAccessible(constructor);
93+
FailureAnalyzer analyzer = (FailureAnalyzer) constructor.newInstance();
94+
if (analyzer instanceof BeanFactoryAware || analyzer instanceof EnvironmentAware) {
95+
if (context == null) {
96+
logger.trace(LogMessage.format("Skipping %s due to missing context", className));
97+
return null;
98+
}
99+
if (analyzer instanceof BeanFactoryAware) {
100+
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
101+
}
102+
if (analyzer instanceof EnvironmentAware) {
103+
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
104+
}
99105
}
106+
return analyzer;
100107
}
101108

102109
@Override

Diff for: spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/FailureAnalyzersTests.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -85,8 +85,19 @@ void analyzerThatFailsDuringAnalysisDoesNotPreventOtherAnalyzersFromBeingCalled(
8585
verify(failureAnalyzer, times(1)).analyze(failure);
8686
}
8787

88+
@Test
89+
void createWithNullContextSkipsAwareAnalyzers() {
90+
RuntimeException failure = new RuntimeException();
91+
analyzeAndReport("basic.factories", failure, null);
92+
verify(failureAnalyzer, times(1)).analyze(failure);
93+
}
94+
8895
private void analyzeAndReport(String factoriesName, Throwable failure) {
8996
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
97+
analyzeAndReport(factoriesName, failure, context);
98+
}
99+
100+
private void analyzeAndReport(String factoriesName, Throwable failure, AnnotationConfigApplicationContext context) {
90101
ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName);
91102
new FailureAnalyzers(context, classLoader).reportException(failure);
92103
}

0 commit comments

Comments
 (0)