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.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
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.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -33,7 +37,17 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* @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
|
||||
protected FailureAnalysis analyze(Throwable rootFailure, BeanCurrentlyInCreationException cause) {
|
||||
|
@ -41,12 +55,18 @@ class BeanCurrentlyInCreationFailureAnalyzer extends AbstractFailureAnalyzer<Bea
|
|||
if (dependencyCycle == null) {
|
||||
return null;
|
||||
}
|
||||
return new FailureAnalysis(buildMessage(dependencyCycle),
|
||||
"Relying upon circular references is discouraged and they are prohibited by default. "
|
||||
+ "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 "
|
||||
+ "spring.main.allow-circular-references to true if you have not already done so.",
|
||||
cause);
|
||||
return new FailureAnalysis(buildMessage(dependencyCycle), action(), cause);
|
||||
}
|
||||
|
||||
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. "
|
||||
+ "As a last resort, it may be possible to break the cycle automatically by setting "
|
||||
+ "spring.main.allow-circular-references to true.";
|
||||
}
|
||||
|
||||
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.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
||||
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.BeanTwoConfiguration;
|
||||
import org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzerTests.CyclicBeanMethodsConfiguration.InnerConfiguration;
|
||||
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.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -47,7 +46,7 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
*/
|
||||
class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||
|
||||
private final FailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
||||
private final BeanCurrentlyInCreationFailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
||||
|
||||
@Test
|
||||
void cyclicBeanMethods() throws IOException {
|
||||
|
@ -131,6 +130,18 @@ class BeanCurrentlyInCreationFailureAnalyzerTests {
|
|||
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 {
|
||||
try (BufferedReader reader = new BufferedReader(new StringReader(analysis.getDescription()))) {
|
||||
return reader.lines().collect(Collectors.toList());
|
||||
|
@ -138,13 +149,23 @@ class BeanCurrentlyInCreationFailureAnalyzerTests {
|
|||
}
|
||||
|
||||
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();
|
||||
return analysis;
|
||||
}
|
||||
|
||||
private Exception createFailure(Class<?> configuration) {
|
||||
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configuration)) {
|
||||
private Exception createFailure(Class<?> configuration, boolean allowCircularReferences) {
|
||||
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");
|
||||
return null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue