Merge branch '1.4.x' into 1.5.x
This commit is contained in:
commit
d2d911ba94
|
|
@ -39,45 +39,96 @@ class BeanCurrentlyInCreationFailureAnalyzer
|
||||||
@Override
|
@Override
|
||||||
protected FailureAnalysis analyze(Throwable rootFailure,
|
protected FailureAnalysis analyze(Throwable rootFailure,
|
||||||
BeanCurrentlyInCreationException cause) {
|
BeanCurrentlyInCreationException cause) {
|
||||||
List<String> beansInCycle = new ArrayList<String>();
|
List<BeanInCycle> beansInCycle = new ArrayList<BeanInCycle>();
|
||||||
Throwable candidate = rootFailure;
|
Throwable candidate = rootFailure;
|
||||||
|
int cycleStart = -1;
|
||||||
while (candidate != null) {
|
while (candidate != null) {
|
||||||
if (candidate instanceof BeanCreationException) {
|
if (candidate instanceof BeanCreationException) {
|
||||||
BeanCreationException creationEx = (BeanCreationException) candidate;
|
BeanCreationException creationEx = (BeanCreationException) candidate;
|
||||||
if (StringUtils.hasText(creationEx.getBeanName())) {
|
if (StringUtils.hasText(creationEx.getBeanName())) {
|
||||||
beansInCycle
|
BeanInCycle beanInCycle = new BeanInCycle(creationEx);
|
||||||
.add(creationEx.getBeanName() + getDescription(creationEx));
|
int index = beansInCycle.indexOf(beanInCycle);
|
||||||
|
if (index == -1) {
|
||||||
|
beansInCycle.add(beanInCycle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cycleStart = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidate = candidate.getCause();
|
candidate = candidate.getCause();
|
||||||
}
|
}
|
||||||
StringBuilder message = new StringBuilder();
|
StringBuilder message = new StringBuilder();
|
||||||
int uniqueBeans = beansInCycle.size() - 1;
|
message.append(String.format("The dependencies of some of the beans in the "
|
||||||
message.append(
|
+ "application context form a cycle:%n%n"));
|
||||||
String.format("There is a circular dependency between %s beans in the "
|
for (int i = 0; i < beansInCycle.size(); i++) {
|
||||||
+ "application context:%n", uniqueBeans));
|
if (i == cycleStart) {
|
||||||
for (String bean : beansInCycle) {
|
message.append(String.format("┌─────┐%n"));
|
||||||
message.append(String.format("\t- %s%n", bean));
|
}
|
||||||
|
else if (i > 0) {
|
||||||
|
message.append(String.format("%s ↓%n", i < cycleStart ? " " : "↑"));
|
||||||
|
}
|
||||||
|
message.append(String.format("%s %s%n", i < cycleStart ? " " : "|",
|
||||||
|
beansInCycle.get(i)));
|
||||||
}
|
}
|
||||||
|
message.append(String.format("└─────┘%n"));
|
||||||
return new FailureAnalysis(message.toString(), null, cause);
|
return new FailureAnalysis(message.toString(), null, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getDescription(BeanCreationException ex) {
|
private static final class BeanInCycle {
|
||||||
if (StringUtils.hasText(ex.getResourceDescription())) {
|
|
||||||
return String.format(" defined in %s", ex.getResourceDescription());
|
|
||||||
}
|
|
||||||
InjectionPoint failedInjectionPoint = findFailedInjectionPoint(ex);
|
|
||||||
if (failedInjectionPoint != null && failedInjectionPoint.getField() != null) {
|
|
||||||
return String.format(" (field %s)", failedInjectionPoint.getField());
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private InjectionPoint findFailedInjectionPoint(BeanCreationException ex) {
|
private final String name;
|
||||||
if (!(ex instanceof UnsatisfiedDependencyException)) {
|
|
||||||
return null;
|
private final String description;
|
||||||
|
|
||||||
|
private BeanInCycle(BeanCreationException ex) {
|
||||||
|
this.name = ex.getBeanName();
|
||||||
|
this.description = determineDescription(ex);
|
||||||
}
|
}
|
||||||
return ((UnsatisfiedDependencyException) ex).getInjectionPoint();
|
|
||||||
|
private String determineDescription(BeanCreationException ex) {
|
||||||
|
if (StringUtils.hasText(ex.getResourceDescription())) {
|
||||||
|
return String.format(" defined in %s", ex.getResourceDescription());
|
||||||
|
}
|
||||||
|
InjectionPoint failedInjectionPoint = findFailedInjectionPoint(ex);
|
||||||
|
if (failedInjectionPoint != null && failedInjectionPoint.getField() != null) {
|
||||||
|
return String.format(" (field %s)", failedInjectionPoint.getField());
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private InjectionPoint findFailedInjectionPoint(BeanCreationException ex) {
|
||||||
|
if (!(ex instanceof UnsatisfiedDependencyException)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ((UnsatisfiedDependencyException) ex).getInjectionPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.name.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BeanInCycle other = (BeanInCycle) obj;
|
||||||
|
return this.name.equals(other.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name + this.description;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,21 @@
|
||||||
|
|
||||||
package org.springframework.boot.diagnostics.analyzer;
|
package org.springframework.boot.diagnostics.analyzer;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||||
import org.springframework.boot.diagnostics.FailureAnalyzer;
|
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.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;
|
||||||
|
|
@ -39,10 +49,25 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
private final FailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
private final FailureAnalyzer analyzer = new BeanCurrentlyInCreationFailureAnalyzer();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cyclicBeanMethods() {
|
public void cyclicBeanMethods() throws IOException {
|
||||||
FailureAnalysis analysis = performAnalysis(CyclicBeanMethodsConfiguration.class);
|
FailureAnalysis analysis = performAnalysis(CyclicBeanMethodsConfiguration.class);
|
||||||
|
List<String> lines = readDescriptionLines(analysis);
|
||||||
|
assertThat(lines).hasSize(9);
|
||||||
|
assertThat(lines.get(0)).isEqualTo(
|
||||||
|
"The dependencies of some of the beans in the application context form a cycle:");
|
||||||
|
assertThat(lines.get(1)).isEqualTo("");
|
||||||
|
assertThat(lines.get(2)).isEqualTo("┌─────┐");
|
||||||
|
assertThat(lines.get(3)).startsWith(
|
||||||
|
"| one defined in " + InnerInnerConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(4)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(5))
|
||||||
|
.startsWith("| two defined in " + InnerConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(6)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(7)).startsWith(
|
||||||
|
"| three defined in " + CyclicBeanMethodsConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(8)).isEqualTo("└─────┘");
|
||||||
assertThat(analysis.getDescription()).startsWith(
|
assertThat(analysis.getDescription()).startsWith(
|
||||||
"There is a circular dependency between 3 beans in the application context:");
|
"The dependencies of some of the beans in the application context form a cycle:");
|
||||||
assertThat(analysis.getDescription()).contains(
|
assertThat(analysis.getDescription()).contains(
|
||||||
"one defined in " + CyclicBeanMethodsConfiguration.class.getName());
|
"one defined in " + CyclicBeanMethodsConfiguration.class.getName());
|
||||||
assertThat(analysis.getDescription()).contains(
|
assertThat(analysis.getDescription()).contains(
|
||||||
|
|
@ -52,22 +77,74 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void cycleWithAutowiredFields() {
|
public void cycleWithAutowiredFields() throws IOException {
|
||||||
FailureAnalysis analysis = performAnalysis(CycleWithAutowiredFields.class);
|
FailureAnalysis analysis = performAnalysis(CycleWithAutowiredFields.class);
|
||||||
assertThat(analysis.getDescription()).startsWith(
|
assertThat(analysis.getDescription()).startsWith(
|
||||||
"There is a circular dependency between 3 beans in the application context:");
|
"The dependencies of some of the beans in the application context form a cycle:");
|
||||||
assertThat(analysis.getDescription()).contains("three defined in "
|
List<String> lines = readDescriptionLines(analysis);
|
||||||
+ CycleWithAutowiredFields.BeanThreeConfiguration.class.getName());
|
assertThat(lines).hasSize(9);
|
||||||
assertThat(analysis.getDescription())
|
assertThat(lines.get(0)).isEqualTo(
|
||||||
.contains("one defined in " + CycleWithAutowiredFields.class.getName());
|
"The dependencies of some of the beans in the application context form a cycle:");
|
||||||
assertThat(analysis.getDescription())
|
assertThat(lines.get(1)).isEqualTo("");
|
||||||
.contains(CycleWithAutowiredFields.BeanTwoConfiguration.class.getName()
|
assertThat(lines.get(2)).isEqualTo("┌─────┐");
|
||||||
+ " (field private " + BeanThree.class.getName());
|
assertThat(lines.get(3)).startsWith(
|
||||||
|
"| three defined in " + BeanThreeConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(4)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(5)).startsWith(
|
||||||
|
"| one defined in " + CycleWithAutowiredFields.class.getName());
|
||||||
|
assertThat(lines.get(6)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(7)).startsWith("| " + BeanTwoConfiguration.class.getName()
|
||||||
|
+ " (field private " + BeanThree.class.getName());
|
||||||
|
assertThat(lines.get(8)).isEqualTo("└─────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void cycleReferencedViaOtherBeans() throws IOException {
|
||||||
|
FailureAnalysis analysis = performAnalysis(
|
||||||
|
CycleReferencedViaOtherBeansConfiguration.class);
|
||||||
|
List<String> lines = readDescriptionLines(analysis);
|
||||||
|
assertThat(lines).hasSize(12);
|
||||||
|
assertThat(lines.get(0)).isEqualTo(
|
||||||
|
"The dependencies of some of the beans in the application context form a cycle:");
|
||||||
|
assertThat(lines.get(1)).isEqualTo("");
|
||||||
|
assertThat(lines.get(2))
|
||||||
|
.contains("refererOne " + "(field " + RefererTwo.class.getName());
|
||||||
|
assertThat(lines.get(3)).isEqualTo(" ↓");
|
||||||
|
assertThat(lines.get(4))
|
||||||
|
.contains("refererTwo " + "(field " + BeanOne.class.getName());
|
||||||
|
assertThat(lines.get(5)).isEqualTo("┌─────┐");
|
||||||
|
assertThat(lines.get(6)).startsWith("| one defined in "
|
||||||
|
+ CycleReferencedViaOtherBeansConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(7)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(8)).startsWith("| two defined in "
|
||||||
|
+ CycleReferencedViaOtherBeansConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(9)).isEqualTo("↑ ↓");
|
||||||
|
assertThat(lines.get(10)).startsWith("| three defined in "
|
||||||
|
+ CycleReferencedViaOtherBeansConfiguration.class.getName());
|
||||||
|
assertThat(lines.get(11)).isEqualTo("└─────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> readDescriptionLines(FailureAnalysis analysis)
|
||||||
|
throws IOException {
|
||||||
|
BufferedReader lineReader = new BufferedReader(
|
||||||
|
new StringReader(analysis.getDescription()));
|
||||||
|
try {
|
||||||
|
List<String> lines = new ArrayList<String>();
|
||||||
|
String line;
|
||||||
|
while ((line = lineReader.readLine()) != null) {
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
lineReader.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FailureAnalysis performAnalysis(Class<?> configuration) {
|
private FailureAnalysis performAnalysis(Class<?> configuration) {
|
||||||
FailureAnalysis analysis = this.analyzer.analyze(createFailure(configuration));
|
FailureAnalysis analysis = this.analyzer.analyze(createFailure(configuration));
|
||||||
assertThat(analysis).isNotNull();
|
assertThat(analysis).isNotNull();
|
||||||
|
System.out.println(analysis.getDescription());
|
||||||
return analysis;
|
return analysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,9 +166,39 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@org.springframework.context.annotation.Configuration
|
||||||
static class CyclicBeanMethodsConfiguration {
|
static class CyclicBeanMethodsConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BeanThree three(BeanOne one) {
|
||||||
|
return new BeanThree();
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.springframework.context.annotation.Configuration
|
||||||
|
static class InnerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BeanTwo two(BeanThree three) {
|
||||||
|
return new BeanTwo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class InnerInnerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public BeanOne one(BeanTwo two) {
|
||||||
|
return new BeanOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class CycleReferencedViaOtherBeansConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BeanOne one(BeanTwo two) {
|
public BeanOne one(BeanTwo two) {
|
||||||
return new BeanOne();
|
return new BeanOne();
|
||||||
|
|
@ -103,13 +210,33 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public BeanThree three(BeanOne one) {
|
public BeanThree three(BeanOne beanOne) {
|
||||||
return new BeanThree();
|
return new BeanThree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class InnerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RefererTwo refererTwo() {
|
||||||
|
return new RefererTwo();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class InnerInnerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RefererOne refererOne() {
|
||||||
|
return new RefererOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@org.springframework.context.annotation.Configuration
|
||||||
public static class CycleWithAutowiredFields {
|
public static class CycleWithAutowiredFields {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
@ -127,6 +254,7 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
public BeanTwo two() {
|
public BeanTwo two() {
|
||||||
return new BeanTwo();
|
return new BeanTwo();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BeanThreeConfiguration {
|
public static class BeanThreeConfiguration {
|
||||||
|
|
@ -135,10 +263,25 @@ public class BeanCurrentlyInCreationFailureAnalyzerTests {
|
||||||
public BeanThree three(BeanOne one) {
|
public BeanThree three(BeanOne one) {
|
||||||
return new BeanThree();
|
return new BeanThree();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class RefererOne {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RefererTwo refererTwo;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RefererTwo {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
BeanOne beanOne;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class BeanOne {
|
static class BeanOne {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue