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.Banner.Mode;
import org.springframework.boot.bind.PropertiesConfigurationFactory; import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.diagnostics.FailureAnalyzers;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
@ -328,7 +327,7 @@ public class SpringApplication {
StopWatch stopWatch = new StopWatch(); StopWatch stopWatch = new StopWatch();
stopWatch.start(); stopWatch.start();
ConfigurableApplicationContext context = null; ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty(); configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args); SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(); listeners.starting();
@ -341,7 +340,8 @@ public class SpringApplication {
bindToSpringApplication(environment); bindToSpringApplication(environment);
Banner printedBanner = printBanner(environment); Banner printedBanner = printBanner(environment);
context = createApplicationContext(); context = createApplicationContext();
analyzers = new FailureAnalyzers(context); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, prepareContext(context, environment, listeners, applicationArguments,
printedBanner); printedBanner);
refreshContext(context); refreshContext(context);
@ -355,7 +355,7 @@ public class SpringApplication {
return context; return context;
} }
catch (Throwable ex) { catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex); handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }
} }
@ -423,11 +423,11 @@ public class SpringApplication {
SpringApplicationRunListener.class, types, this, args)); 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<?>[] {}); 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) { Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates // Use names and ensure unique to protect against duplicates
@ -853,15 +853,15 @@ public class SpringApplication {
} }
private void handleRunFailure(ConfigurableApplicationContext context, private void handleRunFailure(ConfigurableApplicationContext context,
SpringApplicationRunListeners listeners, FailureAnalyzers analyzers, SpringApplicationRunListeners listeners,
Throwable exception) { Collection<SpringBootExceptionReporter> exceptionReporters, Throwable exception) {
try { try {
try { try {
handleExitCode(context, exception); handleExitCode(context, exception);
listeners.finished(context, exception); listeners.finished(context, exception);
} }
finally { finally {
reportFailure(analyzers, exception); reportFailure(exceptionReporters, exception);
if (context != null) { if (context != null) {
context.close(); context.close();
} }
@ -873,13 +873,16 @@ public class SpringApplication {
ReflectionUtils.rethrowRuntimeException(exception); ReflectionUtils.rethrowRuntimeException(exception);
} }
private void reportFailure(FailureAnalyzers analyzers, Throwable failure) { private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters,
Throwable failure) {
try { try {
if (analyzers != null && analyzers.analyzeAndReport(failure)) { for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
registerLoggedException(failure); registerLoggedException(failure);
return; return;
} }
} }
}
catch (Throwable ex) { catch (Throwable ex) {
// Continue with normal handling of the original failure // 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.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.io.support.SpringFactoriesLoader;
@ -44,9 +45,8 @@ import org.springframework.util.ReflectionUtils;
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Phillip Webb * @author Phillip Webb
* @author Stephane Nicoll * @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); private static final Log logger = LogFactory.getLog(FailureAnalyzers.class);
@ -54,12 +54,7 @@ public final class FailureAnalyzers {
private final List<FailureAnalyzer> analyzers; private final List<FailureAnalyzer> analyzers;
/** FailureAnalyzers(ConfigurableApplicationContext context) {
* Create a new {@link FailureAnalyzers} instance.
* @param context the source application context
* @since 1.4.1
*/
public FailureAnalyzers(ConfigurableApplicationContext context) {
this(context, null); this(context, null);
} }
@ -103,12 +98,8 @@ public final class FailureAnalyzers {
} }
} }
/** @Override
* Analyze and report the specified {@code failure}. public boolean reportException(Throwable 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); FailureAnalysis analysis = analyze(failure, this.analyzers);
return report(analysis, this.classLoader); return report(analysis, this.classLoader);
} }

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

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

Loading…
Cancel
Save