Commit 646c20ed authored by Andy Wilkinson's avatar Andy Wilkinson

Merge branch '2.0.x'

parents 467169aa f9b6c1ab
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.diagnostics.analyzer; package org.springframework.boot.autoconfigure.diagnostics.analyzer;
import java.lang.annotation.Annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -30,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory; ...@@ -30,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
...@@ -89,11 +91,23 @@ class NoSuchBeanDefinitionFailureAnalyzer ...@@ -89,11 +91,23 @@ class NoSuchBeanDefinitionFailureAnalyzer
message.append(String.format("%s required %s that could not be found.%n", message.append(String.format("%s required %s that could not be found.%n",
(description != null) ? description : "A component", (description != null) ? description : "A component",
getBeanDescription(cause))); getBeanDescription(cause)));
for (AutoConfigurationResult result : autoConfigurationResults) { List<Annotation> injectionAnnotations = findInjectionAnnotations(rootFailure);
message.append(String.format("\t- %s%n", result)); 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) { if (!autoConfigurationResults.isEmpty() || !userConfigurationResults.isEmpty()) {
message.append(String.format("\t- %s%n", result)); 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.", String action = String.format("Consider %s %s in your configuration.",
(!autoConfigurationResults.isEmpty() (!autoConfigurationResults.isEmpty()
...@@ -164,7 +178,7 @@ class NoSuchBeanDefinitionFailureAnalyzer ...@@ -164,7 +178,7 @@ class NoSuchBeanDefinitionFailureAnalyzer
if (!conditionAndOutcome.getOutcome().isMatch()) { if (!conditionAndOutcome.getOutcome().isMatch()) {
for (MethodMetadata method : methods) { for (MethodMetadata method : methods) {
results.add(new AutoConfigurationResult(method, results.add(new AutoConfigurationResult(method,
conditionAndOutcome.getOutcome(), source.isMethod())); conditionAndOutcome.getOutcome()));
} }
} }
} }
...@@ -179,11 +193,21 @@ class NoSuchBeanDefinitionFailureAnalyzer ...@@ -179,11 +193,21 @@ class NoSuchBeanDefinitionFailureAnalyzer
String message = String.format("auto-configuration '%s' was excluded", String message = String.format("auto-configuration '%s' was excluded",
ClassUtils.getShortName(excludedClass)); ClassUtils.getShortName(excludedClass));
results.add(new AutoConfigurationResult(method, 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 class Source {
private final String className; private final String className;
...@@ -204,10 +228,6 @@ class NoSuchBeanDefinitionFailureAnalyzer ...@@ -204,10 +228,6 @@ class NoSuchBeanDefinitionFailureAnalyzer
return this.methodName; return this.methodName;
} }
public boolean isMethod() {
return this.methodName != null;
}
} }
private class BeanMethods implements Iterable<MethodMetadata> { private class BeanMethods implements Iterable<MethodMetadata> {
...@@ -295,26 +315,17 @@ class NoSuchBeanDefinitionFailureAnalyzer ...@@ -295,26 +315,17 @@ class NoSuchBeanDefinitionFailureAnalyzer
private final ConditionOutcome conditionOutcome; private final ConditionOutcome conditionOutcome;
private final boolean methodEvaluated;
AutoConfigurationResult(MethodMetadata methodMetadata, AutoConfigurationResult(MethodMetadata methodMetadata,
ConditionOutcome conditionOutcome, boolean methodEvaluated) { ConditionOutcome conditionOutcome) {
this.methodMetadata = methodMetadata; this.methodMetadata = methodMetadata;
this.conditionOutcome = conditionOutcome; this.conditionOutcome = conditionOutcome;
this.methodEvaluated = methodEvaluated;
} }
@Override @Override
public String toString() { public String toString() {
if (this.methodEvaluated) { return String.format("Bean method '%s' in '%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());
}
return String.format("Bean method '%s' not loaded because %s",
this.methodMetadata.getMethodName(), this.methodMetadata.getMethodName(),
ClassUtils.getShortName(this.methodMetadata.getDeclaringClassName()),
this.conditionOutcome.getMessage()); this.conditionOutcome.getMessage());
} }
......
...@@ -25,6 +25,7 @@ import org.springframework.beans.DirectFieldAccessor; ...@@ -25,6 +25,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.FatalBeanException; import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
...@@ -101,7 +102,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -101,7 +102,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
assertDescriptionConstructorMissingType(analysis, StringHandler.class, 0, assertDescriptionConstructorMissingType(analysis, StringHandler.class, 0,
String.class); String.class);
assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'", assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'",
"string"); "string", ClassUtils.getShortName(TestTypeClassAutoConfiguration.class));
assertActionMissingType(analysis, String.class); assertActionMissingType(analysis, String.class);
} }
...@@ -116,7 +117,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -116,7 +117,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
.getShortName(TestPropertyAutoConfiguration.class.getName()); .getShortName(TestPropertyAutoConfiguration.class.getName());
assertClassDisabled(analysis, assertClassDisabled(analysis,
String.format("auto-configuration '%s' was excluded", configClass), String.format("auto-configuration '%s' was excluded", configClass),
"string"); "string", ClassUtils.getShortName(TestPropertyAutoConfiguration.class));
assertActionMissingType(analysis, String.class); assertActionMissingType(analysis, String.class);
} }
...@@ -130,7 +131,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -130,7 +131,7 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
"did not find property 'spring.string.enabled'", "did not find property 'spring.string.enabled'",
TestPropertyAutoConfiguration.class, "string"); TestPropertyAutoConfiguration.class, "string");
assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'", assertClassDisabled(analysis, "did not find required class 'com.example.FooBar'",
"string"); "string", ClassUtils.getShortName(TestPropertyAutoConfiguration.class));
assertActionMissingType(analysis, String.class); assertActionMissingType(analysis, String.class);
} }
...@@ -169,6 +170,14 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -169,6 +170,14 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
assertActionMissingType(analysis, String.class); 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, private void assertDescriptionConstructorMissingType(FailureAnalysis analysis,
Class<?> component, int index, Class<?> type) { Class<?> component, int index, Class<?> type) {
String expected = String.format( String expected = String.format(
...@@ -201,9 +210,9 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -201,9 +210,9 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests {
} }
private void assertClassDisabled(FailureAnalysis analysis, String description, private void assertClassDisabled(FailureAnalysis analysis, String description,
String methodName) { String methodName, String className) {
String expected = String.format("Bean method '%s' not loaded because", String expected = String.format("Bean method '%s' in '%s' not loaded because",
methodName); methodName, className);
assertThat(analysis.getDescription()).contains(expected); assertThat(analysis.getDescription()).contains(expected);
assertThat(analysis.getDescription()).contains(description); assertThat(analysis.getDescription()).contains(description);
} }
...@@ -340,6 +349,25 @@ public class NoSuchBeanDefinitionFailureAnalyzerTests { ...@@ -340,6 +349,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 { protected static class StringHandler {
public StringHandler(String foo) { public StringHandler(String foo) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment