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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user