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
pull/23706/head
Phillip Webb 4 years ago
parent 84f96033c5
commit f89b99bdbc

@ -312,7 +312,6 @@ public class SpringApplication {
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
@ -323,8 +322,6 @@ public class SpringApplication {
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
@ -336,7 +333,7 @@ public class SpringApplication {
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
@ -344,7 +341,7 @@ public class SpringApplication {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
@ -812,7 +809,7 @@ public class SpringApplication {
}
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
@ -821,7 +818,7 @@ public class SpringApplication {
}
}
finally {
reportFailure(exceptionReporters, exception);
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
}
@ -833,6 +830,16 @@ public class SpringApplication {
ReflectionUtils.rethrowRuntimeException(exception);
}
private Collection<SpringBootExceptionReporter> getExceptionReporters(ConfigurableApplicationContext context) {
try {
return getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class<?>[] { ConfigurableApplicationContext.class }, context);
}
catch (Throwable ex) {
return Collections.emptyList();
}
}
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
try {
for (SpringBootExceptionReporter reporter : exceptionReporters) {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,7 +31,6 @@ import org.springframework.context.EnvironmentAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@ -61,42 +60,50 @@ final class FailureAnalyzers implements SpringBootExceptionReporter {
}
FailureAnalyzers(ConfigurableApplicationContext context, ClassLoader classLoader) {
Assert.notNull(context, "Context must not be null");
this.classLoader = (classLoader != null) ? classLoader : context.getClassLoader();
this.analyzers = loadFailureAnalyzers(this.classLoader);
prepareFailureAnalyzers(this.analyzers, context);
this.classLoader = (classLoader != null) ? classLoader : getClassLoader(context);
this.analyzers = loadFailureAnalyzers(context, this.classLoader);
}
private List<FailureAnalyzer> loadFailureAnalyzers(ClassLoader classLoader) {
List<String> analyzerNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);
private ClassLoader getClassLoader(ConfigurableApplicationContext context) {
return (context != null) ? context.getClassLoader() : null;
}
private List<FailureAnalyzer> loadFailureAnalyzers(ConfigurableApplicationContext context,
ClassLoader classLoader) {
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(FailureAnalyzer.class, classLoader);
List<FailureAnalyzer> analyzers = new ArrayList<>();
for (String analyzerName : analyzerNames) {
for (String className : classNames) {
try {
Constructor<?> constructor = ClassUtils.forName(analyzerName, classLoader).getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
analyzers.add((FailureAnalyzer) constructor.newInstance());
FailureAnalyzer analyzer = createAnalyzer(context, className);
if (analyzer != null) {
analyzers.add(analyzer);
}
}
catch (Throwable ex) {
logger.trace(LogMessage.format("Failed to load %s", analyzerName), ex);
logger.trace(LogMessage.format("Failed to load %s", className), ex);
}
}
AnnotationAwareOrderComparator.sort(analyzers);
return analyzers;
}
private void prepareFailureAnalyzers(List<FailureAnalyzer> analyzers, ConfigurableApplicationContext context) {
for (FailureAnalyzer analyzer : analyzers) {
prepareAnalyzer(context, analyzer);
}
}
private void prepareAnalyzer(ConfigurableApplicationContext context, FailureAnalyzer analyzer) {
if (analyzer instanceof BeanFactoryAware) {
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
}
if (analyzer instanceof EnvironmentAware) {
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
private FailureAnalyzer createAnalyzer(ConfigurableApplicationContext context, String className) throws Exception {
Constructor<?> constructor = ClassUtils.forName(className, this.classLoader).getDeclaredConstructor();
ReflectionUtils.makeAccessible(constructor);
FailureAnalyzer analyzer = (FailureAnalyzer) constructor.newInstance();
if (analyzer instanceof BeanFactoryAware || analyzer instanceof EnvironmentAware) {
if (context == null) {
logger.trace(LogMessage.format("Skipping %s due to missing context", className));
return null;
}
if (analyzer instanceof BeanFactoryAware) {
((BeanFactoryAware) analyzer).setBeanFactory(context.getBeanFactory());
}
if (analyzer instanceof EnvironmentAware) {
((EnvironmentAware) analyzer).setEnvironment(context.getEnvironment());
}
}
return analyzer;
}
@Override

@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -85,8 +85,19 @@ class FailureAnalyzersTests {
verify(failureAnalyzer, times(1)).analyze(failure);
}
@Test
void createWithNullContextSkipsAwareAnalyzers() {
RuntimeException failure = new RuntimeException();
analyzeAndReport("basic.factories", failure, null);
verify(failureAnalyzer, times(1)).analyze(failure);
}
private void analyzeAndReport(String factoriesName, Throwable failure) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
analyzeAndReport(factoriesName, failure, context);
}
private void analyzeAndReport(String factoriesName, Throwable failure, AnnotationConfigApplicationContext context) {
ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName);
new FailureAnalyzers(context, classLoader).reportException(failure);
}

Loading…
Cancel
Save