Fix FailureAnalyzers tangle

Create a new `SpringBootExceptionReporter` interface so that a direct
link between `SpringApplication` and `FailureAnalyzers` is no longer
needed.

This prevents package tangle warnings and allows for cleaner separation
of concerns.

Fixes gh-8612
pull/8621/head
Phillip Webb 8 years ago
parent 69b72874ea
commit 2a9bbfdb86

@ -42,7 +42,6 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.Banner.Mode;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.diagnostics.FailureAnalyzers;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
@ -328,7 +327,7 @@ public class SpringApplication {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
@ -341,7 +340,8 @@ public class SpringApplication {
bindToSpringApplication(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
@ -355,7 +355,7 @@ public class SpringApplication {
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
@ -423,11 +423,11 @@ public class SpringApplication {
SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
@ -853,15 +853,15 @@ public class SpringApplication {
}
private void handleRunFailure(ConfigurableApplicationContext context,
SpringApplicationRunListeners listeners, FailureAnalyzers analyzers,
Throwable exception) {
SpringApplicationRunListeners listeners,
Collection<SpringBootExceptionReporter> exceptionReporters, Throwable exception) {
try {
try {
handleExitCode(context, exception);
listeners.finished(context, exception);
}
finally {
reportFailure(analyzers, exception);
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
@ -873,13 +873,16 @@ public class SpringApplication {
ReflectionUtils.rethrowRuntimeException(exception);
}
private void reportFailure(FailureAnalyzers analyzers, Throwable failure) {
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters,
Throwable failure) {
try {
if (analyzers != null && analyzers.analyzeAndReport(failure)) {
for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
registerLoggedException(failure);
return;
}
}
}
catch (Throwable ex) {
// Continue with normal handling of the original failure
}

@ -0,0 +1,43 @@
/*
* Copyright 2012-2017 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Callback interface used to support custom reporting of {@link SpringApplication}
* startup errors. {@link SpringBootExceptionReporter reporters} are loaded via the
* {@link SpringFactoriesLoader} and must declare a public constructor with a single
* {@link ConfigurableApplicationContext} parameter.
*
* @author Phillip Webb
* @since 2.0.0
* @see ApplicationContextAware
*/
public interface SpringBootExceptionReporter {
/**
* Report a startup failure to the user.
* @param failure the source failure
* @return {@code true} if the failure was reported or {@code false} if default
* reporting should occur.
*/
boolean reportException(Throwable failure);
}

@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.support.SpringFactoriesLoader;
@ -44,9 +45,8 @@ import org.springframework.util.ReflectionUtils;
* @author Andy Wilkinson
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.4.0
*/
public final class FailureAnalyzers {
final class FailureAnalyzers implements SpringBootExceptionReporter {
private static final Log logger = LogFactory.getLog(FailureAnalyzers.class);
@ -54,12 +54,7 @@ public final class FailureAnalyzers {
private final List<FailureAnalyzer> analyzers;
/**
* Create a new {@link FailureAnalyzers} instance.
* @param context the source application context
* @since 1.4.1
*/
public FailureAnalyzers(ConfigurableApplicationContext context) {
FailureAnalyzers(ConfigurableApplicationContext context) {
this(context, null);
}
@ -103,12 +98,8 @@ public final class FailureAnalyzers {
}
}
/**
* 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) {
@Override
public boolean reportException(Throwable failure) {
FailureAnalysis analysis = analyze(failure, this.analyzers);
return report(analysis, this.classLoader);
}

@ -7,6 +7,10 @@ org.springframework.boot.env.YamlPropertySourceLoader
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\

@ -78,7 +78,7 @@ public class FailureAnalyzersTests {
private void analyzeAndReport(String factoriesName, Throwable failure) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
ClassLoader classLoader = new CustomSpringFactoriesClassLoader(factoriesName);
new FailureAnalyzers(context, classLoader).analyzeAndReport(failure);
new FailureAnalyzers(context, classLoader).reportException(failure);
}
static class BasicFailureAnalyzer implements FailureAnalyzer {

Loading…
Cancel
Save