Include more information in NoSuchBeanDefinition failure analysis

Closes gh-13594
pull/14774/head
Andy Wilkinson 6 years ago
parent 9e14fc6b8b
commit f9b6c1ab4d

@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.diagnostics.analyzer;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -30,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@ -89,11 +91,23 @@ class NoSuchBeanDefinitionFailureAnalyzer
message.append(String.format("%s required %s that could not be found.%n",
(description != null) ? description : "A component",
getBeanDescription(cause)));
for (AutoConfigurationResult result : autoConfigurationResults) {
message.append(String.format("\t- %s%n", result));
List<Annotation> injectionAnnotations = findInjectionAnnotations(rootFailure);
if (!injectionAnnotations.isEmpty()) {
message.append(String
.format("%nThe injection point has the following annotations:%n"));
for (Annotation injectionAnnotation : injectionAnnotations) {
message.append(String.format("\t- %s%n", injectionAnnotation));
}
}
for (UserConfigurationResult result : userConfigurationResults) {
message.append(String.format("\t- %s%n", result));
if (!autoConfigurationResults.isEmpty() || !userConfigurationResults.isEmpty()) {
message.append(String.format(
"%nThe following candidates were found but could not be injected:%n"));
for (AutoConfigurationResult result : autoConfigurationResults) {
message.append(String.format("\t- %s%n", result));
}
for (UserConfigurationResult result : userConfigurationResults) {
message.append(String.format("\t- %s%n", result));
}
}
String action = String.format("Consider %s %s in your configuration.",
(!autoConfigurationResults.isEmpty()
@ -172,7 +186,7 @@ class NoSuchBeanDefinitionFailureAnalyzer
if (!conditionAndOutcome.getOutcome().isMatch()) {
for (MethodMetadata method : methods) {
results.add(new AutoConfigurationResult(method,
conditionAndOutcome.getOutcome(), source.isMethod()));
conditionAndOutcome.getOutcome()));
}
}
}
@ -187,11 +201,21 @@ class NoSuchBeanDefinitionFailureAnalyzer
String message = String.format("auto-configuration '%s' was excluded",
ClassUtils.getShortName(excludedClass));
results.add(new AutoConfigurationResult(method,
new ConditionOutcome(false, message), false));
new ConditionOutcome(false, message)));
}
}
}
private List<Annotation> findInjectionAnnotations(Throwable failure) {
UnsatisfiedDependencyException unsatisfiedDependencyException = findCause(failure,
UnsatisfiedDependencyException.class);
if (unsatisfiedDependencyException == null) {
return Collections.emptyList();
}
return Arrays.asList(
unsatisfiedDependencyException.getInjectionPoint().getAnnotations());
}
private class Source {
private final String className;
@ -212,10 +236,6 @@ class NoSuchBeanDefinitionFailureAnalyzer
return this.methodName;
}
public boolean isMethod() {
return this.methodName != null;
}
}
private class BeanMethods implements Iterable<MethodMetadata> {
@ -303,26 +323,17 @@ class NoSuchBeanDefinitionFailureAnalyzer
private final ConditionOutcome conditionOutcome;
private final boolean methodEvaluated;
AutoConfigurationResult(MethodMetadata methodMetadata,
ConditionOutcome conditionOutcome, boolean methodEvaluated) {
ConditionOutcome conditionOutcome) {
this.methodMetadata = methodMetadata;
this.conditionOutcome = conditionOutcome;
this.methodEvaluated = methodEvaluated;
}
@Override
public String toString() {
if (this.methodEvaluated) {
return String.format("Bean method '%s' in '%s' not loaded because %s",
this.methodMetadata.getMethodName(),
ClassUtils.getShortName(
this.methodMetadata.getDeclaringClassName()),
this.conditionOutcome.getMessage());
}
return String.format("Bean method '%s' not loaded because %s",
return String.format("Bean method '%s' in '%s' not loaded because %s",
this.methodMetadata.getMethodName(),
ClassUtils.getShortName(this.methodMetadata.getDeclaringClassName()),
this.conditionOutcome.getMessage());
}

@ -27,6 +27,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
@ -127,7 +128,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
assertDescriptionConstructorMissingType(analysis, StringHandler.class, 0,
String.class);
assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'",
"string");
"string", ClassUtils.getShortName(TestTypeClassAutoConfiguration.class));
assertActionMissingType(analysis, String.class);
}
@ -142,7 +143,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
.getShortName(TestPropertyAutoConfiguration.class.getName());
assertClassDisabled(analysis,
String.format("auto-configuration '%s' was excluded", configClass),
"string");
"string", ClassUtils.getShortName(TestPropertyAutoConfiguration.class));
assertActionMissingType(analysis, String.class);
}
@ -156,7 +157,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
"did not find property 'spring.string.enabled'",
TestPropertyAutoConfiguration.class, "string");
assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'",
"string");
"string", ClassUtils.getShortName(TestPropertyAutoConfiguration.class));
assertActionMissingType(analysis, String.class);
}
@ -195,6 +196,14 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
assertActionMissingType(analysis, String.class);
}
@Test
public void failureAnalysisForUnmatchedQualfier() {
FailureAnalysis analysis = analyzeFailure(
createFailure(QualifiedBeanConfiguration.class));
assertThat(analysis.getDescription()).contains(
"@org.springframework.beans.factory.annotation.Qualifier(value=alpha)");
}
private void assertDescriptionConstructorMissingType(FailureAnalysis analysis,
Class<?> component, int index, Class<?> type) {
String expected = String.format(
@ -227,9 +236,9 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
}
private void assertClassDisabled(FailureAnalysis analysis, String description,
String methodName) {
String expected = String.format("Bean method '%s' not loaded because",
methodName);
String methodName, String className) {
String expected = String.format("Bean method '%s' in '%s' not loaded because",
methodName, className);
assertThat(analysis.getDescription()).contains(expected);
assertThat(analysis.getDescription()).contains(description);
}
@ -380,6 +389,25 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
}
@Configuration
public static class QualifiedBeanConfiguration {
@Bean
public String consumer(@Qualifier("alpha") Thing thing) {
return "consumer";
}
@Bean
public Thing producer() {
return new Thing();
}
class Thing {
}
}
protected static class StringHandler {
public StringHandler(String foo) {

Loading…
Cancel
Save