Initialize FailureAnalyzer earlier

This commit changes `FailureAnalyzers` so that the loaded
`FailureAnalyzer` instances are initialized before the context eventually
fails.

Before this commit, the `BeanFactoryAware` callback was processed at a
time where the context has already shutdown. Some implementations need
to access components that are no longer available.

Instead, we now initialize them before the context is refreshed.

Closes gh-6748
This commit is contained in:
Stephane Nicoll
2016-08-25 13:33:55 +02:00
parent 07a50bb16c
commit 25e72fab71
3 changed files with 55 additions and 18 deletions

View File

@@ -298,6 +298,7 @@ public class SpringApplication {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
@@ -308,6 +309,7 @@ public class SpringApplication {
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
@@ -321,7 +323,7 @@ public class SpringApplication {
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, ex);
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
@@ -803,14 +805,15 @@ public class SpringApplication {
}
private void handleRunFailure(ConfigurableApplicationContext context,
SpringApplicationRunListeners listeners, Throwable exception) {
SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
Throwable exception) {
try {
try {
handleExitCode(context, exception);
listeners.finished(context, exception);
}
finally {
reportFailure(exception, context);
reportFailure(analyzers, exception);
if (context != null) {
context.close();
}
@@ -822,11 +825,9 @@ public class SpringApplication {
ReflectionUtils.rethrowRuntimeException(exception);
}
private void reportFailure(Throwable failure,
ConfigurableApplicationContext context) {
private void reportFailure(FailureAnalyzers analyzers, Throwable failure) {
try {
if (FailureAnalyzers.analyzeAndReport(failure, getClass().getClassLoader(),
context)) {
if (analyzers != null && analyzers.analyzeAndReport(failure)) {
registerLoggedException(failure);
return;
}

View File

@@ -42,22 +42,50 @@ import org.springframework.util.ReflectionUtils;
*
* @author Andy Wilkinson
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.4.0
*/
public final class FailureAnalyzers {
private static final Log log = LogFactory.getLog(FailureAnalyzers.class);
private FailureAnalyzers() {
private final ClassLoader classLoader;
private final List<FailureAnalyzer> analyzers;
public FailureAnalyzers(ConfigurableApplicationContext context) {
this.classLoader = context.getClassLoader();
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
}
/**
* Analyze and report the specified {@code failure}.
*
* @param failure the failure to analyze
* @return {@code true} if the failure was handled
*/
public boolean analyzeAndReport(Throwable failure) {
FailureAnalysis analysis = analyze(failure, this.analyzers);
return report(analysis, this.classLoader);
}
/**
* Analyze and report the specified {@code failure}.
*
* @param failure the failure to analyze
* @param classLoader the classloader to use
* @param context the context to use
* @return {@code true} if the failure was handled
* @deprecated in favour of {@link #analyzeAndReport(Throwable)}
*/
@Deprecated
public static boolean analyzeAndReport(Throwable failure, ClassLoader classLoader,
ConfigurableApplicationContext context) {
List<FailureAnalyzer> analyzers = loadFailureAnalyzers(classLoader);
List<FailureAnalysisReporter> reporters = SpringFactoriesLoader
.loadFactories(FailureAnalysisReporter.class, classLoader);
FailureAnalysis analysis = analyze(failure, analyzers, context);
return report(analysis, reporters);
prepareFailureAnalyzers(analyzers, context);
FailureAnalysis analysis = analyze(failure, analyzers);
return report(analysis, classLoader);
}
private static List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
@@ -79,10 +107,16 @@ public final class FailureAnalyzers {
return analyzers;
}
private static FailureAnalysis analyze(Throwable failure,
List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) {
private static void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers,
ConfigurableApplicationContext context) {
for (FailureAnalyzer analyzer : analyzers) {
prepareAnalyzer(context, analyzer);
}
}
private static FailureAnalysis analyze(Throwable failure,
List<FailureAnalyzer> analyzers) {
for (FailureAnalyzer analyzer : analyzers) {
FailureAnalysis analysis = analyzer.analyze(failure);
if (analysis != null) {
return analysis;
@@ -99,7 +133,9 @@ public final class FailureAnalyzers {
}
private static boolean report(FailureAnalysis analysis,
List<FailureAnalysisReporter> reporters) {
ClassLoader classLoader) {
List<FailureAnalysisReporter> reporters = SpringFactoriesLoader
.loadFactories(FailureAnalysisReporter.class, classLoader);
if (analysis == null || reporters.isEmpty()) {
return false;
}

View File

@@ -70,8 +70,7 @@ public class FailureAnalyzersTests {
private void analyzeAndReport(final String factoriesName, Throwable failure) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
FailureAnalyzers.analyzeAndReport(failure,
context.setClassLoader(
new ClassLoader(getClass().getClassLoader()) {
@Override
@@ -85,7 +84,8 @@ public class FailureAnalyzersTests {
}
}
}, context);
});
new FailureAnalyzers(context).analyzeAndReport(failure);
}
static class BasicFailureAnalyzer implements FailureAnalyzer {