Improve failure analysis action when circular references are allowed
Closes gh-27735
This commit is contained in:
parent
42ef97b9ec
commit
31d88c3d3c
|
@ -19,10 +19,14 @@ package org.springframework.boot.diagnostics.analyzer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.beans.factory.InjectionPoint;
|
import org.springframework.beans.factory.InjectionPoint;
|
||||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||||
|
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
@ -33,7 +37,17 @@ import org.springframework.util.StringUtils;
|
||||||
*
|
*
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
class BeanCurrentlyInCreationFailureAnalyzer extends AbstractFailureAnalyzer<BeanCurrentlyInCreationException> {
|
class BeanCurrentlyInCreationFailureAnalyzer extends AbstractFailureAnalyzer<BeanCurrentlyInCreationException>
|
||||||
|
implements BeanFactoryAware {
|
||||||
|
|
||||||
|
private AbstractAutowireCapableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||||
|
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
|
||||||
|
this.beanFactory = (AbstractAutowireCapableBeanFactory) beanFactory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FailureAnalysis analyze(Throwable rootFailure, BeanCurrentlyInCreationException cause) {
|
protected FailureAnalysis analyze(Throwable rootFailure, BeanCurrentlyInCreationException cause) {
|
||||||
|
@ -41,12 +55,18 @@ class BeanCurrentlyInCreationFailureAnalyzer extends AbstractFailureAnalyzer<Bea
|
||||||
if (dependencyCycle == null) {
|
if (dependencyCycle == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new FailureAnalysis(buildMessage(dependencyCycle),
|
return new FailureAnalysis(buildMessage(dependencyCycle), action(), cause);
|
||||||
"Relying upon circular references is discouraged and they are prohibited by default. "
|
}
|
||||||
|
|
||||||
|
private String action() {
|
||||||
|
if (this.beanFactory != null && this.beanFactory.isAllowCircularReferences()) {
|
||||||
|
return "Despite circular references being allowed, the dependency cycle between beans could not be "
|
||||||
|
+ "broken. Update your application to remove the dependency cycle.";
|
||||||
|
}
|
||||||
|
return "Relying upon circular references is discouraged and they are prohibited by default. "
|
||||||
+ "Update your application to remove the dependency cycle between beans. "
|
+ "Update your application to remove the dependency cycle between beans. "
|
||||||
+ "As a last resort, it may be possible to break the cycle automatically by setting "
|
+ "As a last resort, it may be possible to break the cycle automatically by setting "
|
||||||
+ "spring.main.allow-circular-references to true if you have not already done so.",
|
+ "spring.main.allow-circular-references to true.";
|
||||||
cause);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DependencyCycle findCycle(Throwable rootFailure) {
|
private DependencyCycle findCycle(Throwable rootFailure) {
|
||||||
|
|
|
@ -26,13 +26,12 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalyzer;
|
|
||||||
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanThreeConfiguration;
|
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanThreeConfiguration;
|
||||||
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanTwoConfiguration;
|
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CycleWithAutowiredFields.BeanTwoConfiguration;
|
||||||
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration;
|
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration;
|
||||||
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration.InnerInnerConfiguration;
|
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration.InnerInnerConfiguration;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -47,7 +46,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||||
*/
|
*/
|
||||||
class BeanCurrentlyInCreationFailureAnalyzerTests {
|
class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
|
|
||||||
private final FailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
private final BeanCurrentlyInCreationFailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void cyclicBeanMethods() throws IOException {
|
void cyclicBeanMethods() throws IOException {
|
||||||
|
@ -131,6 +130,18 @@ class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
assertThat(this.analyzer.analyze(new BeanCurrentlyInCreationException("test"))).isNull();
|
assertThat(this.analyzer.analyze(new BeanCurrentlyInCreationException("test"))).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cycleWithCircularReferencesAllowed() throws IOException {
|
||||||
|
FailureAnalysis analysis = performAnalysis(CyclicBeanMethodsConfiguration.class, true);
|
||||||
|
assertThat(analysis.getAction()).contains("Despite circular references being allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cycleWithCircularReferencesProhibited() throws IOException {
|
||||||
|
FailureAnalysis analysis = performAnalysis(CyclicBeanMethodsConfiguration.class, false);
|
||||||
|
assertThat(analysis.getAction()).contains("As a last resort");
|
||||||
|
}
|
||||||
|
|
||||||
private List<String> readDescriptionLines(FailureAnalysis analysis) throws IOException {
|
private List<String> readDescriptionLines(FailureAnalysis analysis) throws IOException {
|
||||||
try (BufferedReader reader = new BufferedReader(new StringReader(analysis.getDescription()))) {
|
try (BufferedReader reader = new BufferedReader(new StringReader(analysis.getDescription()))) {
|
||||||
return reader.lines().collect(Collectors.toList());
|
return reader.lines().collect(Collectors.toList());
|
||||||
|
@ -138,13 +149,23 @@ class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private FailureAnalysis performAnalysis(Class<?> configuration) {
|
private FailureAnalysis performAnalysis(Class<?> configuration) {
|
||||||
FailureAnalysis analysis = this.analyzer.analyze(createFailure(configuration));
|
return performAnalysis(configuration, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FailureAnalysis performAnalysis(Class<?> configuration, boolean allowCircularReferences) {
|
||||||
|
FailureAnalysis analysis = this.analyzer.analyze(createFailure(configuration, allowCircularReferences));
|
||||||
assertThat(analysis).isNotNull();
|
assertThat(analysis).isNotNull();
|
||||||
return analysis;
|
return analysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Exception createFailure(Class<?> configuration) {
|
private Exception createFailure(Class<?> configuration, boolean allowCircularReferences) {
|
||||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configuration)) {
|
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
|
||||||
|
context.register(configuration);
|
||||||
|
AbstractAutowireCapableBeanFactory beanFactory = (AbstractAutowireCapableBeanFactory) context
|
||||||
|
.getBeanFactory();
|
||||||
|
this.analyzer.setBeanFactory(beanFactory);
|
||||||
|
beanFactory.setAllowCircularReferences(allowCircularReferences);
|
||||||
|
context.refresh();
|
||||||
fail("Expected failure did not occur");
|
fail("Expected failure did not occur");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue