diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 81fb3587a0e..b2c01dce5a2 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -139,7 +139,7 @@ 1.17 5.5.0 1.0-groovy-2.4 - 4.3.0.RC1 + 4.3.0.BUILD-SNAPSHOT 1.6.0.M2 1.2.2.RELEASE 3.0.6.RELEASE diff --git a/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzer.java b/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzer.java similarity index 73% rename from spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzer.java rename to spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzer.java index e6d795bfbd1..61cbfdcd40a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzer.java +++ b/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzer.java @@ -16,6 +16,7 @@ package org.springframework.boot.diagnostics.analyzer; +import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -28,6 +29,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -36,7 +38,7 @@ import org.springframework.util.StringUtils; * * @author Andy Wilkinson */ -class NoUniqueBeanDefinitionExceptionFailureAnalyzer +class NoUniqueBeanDefinitionFailureAnalyzer extends AbstractFailureAnalyzer implements BeanFactoryAware { @@ -51,9 +53,8 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer @Override protected FailureAnalysis analyze(Throwable rootFailure, NoUniqueBeanDefinitionException cause) { - UnsatisfiedDependencyException unsatisfiedDependency = findUnsatisfiedDependencyException( - rootFailure); - if (unsatisfiedDependency == null) { + String consumerDescription = getConsumerDescription(rootFailure); + if (consumerDescription == null) { return null; } String[] beanNames = extractBeanNames(cause); @@ -62,7 +63,7 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer } StringBuilder message = new StringBuilder(); message.append(String.format("%s required a single bean, but %d were found:%n", - getConsumerDescription(unsatisfiedDependency), beanNames.length)); + consumerDescription, beanNames.length)); for (String beanName : beanNames) { try { BeanDefinition beanDefinition = this.beanFactory @@ -90,13 +91,37 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer cause); } + private String getConsumerDescription(Throwable ex) { + UnsatisfiedDependencyException unsatisfiedDependency = findUnsatisfiedDependencyException( + ex); + if (unsatisfiedDependency != null) { + return getConsumerDescription(unsatisfiedDependency); + } + BeanInstantiationException beanInstantiationException = findBeanInstantiationException( + ex); + if (beanInstantiationException != null) { + return getConsumerDescription(beanInstantiationException); + } + return null; + } + private UnsatisfiedDependencyException findUnsatisfiedDependencyException( Throwable root) { + return findMostNestedCause(root, UnsatisfiedDependencyException.class); + } + + private BeanInstantiationException findBeanInstantiationException(Throwable root) { + return findMostNestedCause(root, BeanInstantiationException.class); + } + + @SuppressWarnings("unchecked") + private T findMostNestedCause(Throwable root, + Class causeType) { Throwable candidate = root; - UnsatisfiedDependencyException mostNestedMatch = null; + T mostNestedMatch = null; while (candidate != null) { - if (candidate instanceof UnsatisfiedDependencyException) { - mostNestedMatch = (UnsatisfiedDependencyException) candidate; + if (causeType.isAssignableFrom(candidate.getClass())) { + mostNestedMatch = (T) candidate; } candidate = candidate.getCause(); } @@ -107,7 +132,7 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer InjectionPoint injectionPoint = ex.getInjectionPoint(); if (injectionPoint != null) { if (injectionPoint.getField() != null) { - return String.format("Field '%s' in %s", + return String.format("Field %s in %s", injectionPoint.getField().getName(), injectionPoint.getField().getDeclaringClass().getName()); } @@ -118,7 +143,7 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer injectionPoint.getMethodParameter().getDeclaringClass() .getName()); } - return String.format("Parameter %d of method '%s' in %s", + return String.format("Parameter %d of method %s in %s", injectionPoint.getMethodParameter().getParameterIndex(), injectionPoint.getMethodParameter().getMethod().getName(), injectionPoint.getMethodParameter().getDeclaringClass() @@ -128,6 +153,18 @@ class NoUniqueBeanDefinitionExceptionFailureAnalyzer return ex.getResourceDescription(); } + private String getConsumerDescription(BeanInstantiationException ex) { + if (ex.getConstructingMethod() != null) { + return String.format("Method %s in %s", ex.getConstructingMethod().getName(), + ex.getConstructingMethod().getDeclaringClass().getName()); + } + if (ex.getConstructor() != null) { + return String.format("Constructor in %s", ClassUtils + .getUserClass(ex.getConstructor().getDeclaringClass()).getName()); + } + return ex.getBeanClass().getName(); + } + private String[] extractBeanNames(NoUniqueBeanDefinitionException cause) { if (cause.getMessage().indexOf("but found") > -1) { return StringUtils.commaDelimitedListToStringArray(cause.getMessage() diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories index 0eb778ab8b2..ca7babae93f 100644 --- a/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -33,8 +33,10 @@ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ +org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ +org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ -org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer +org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer, # FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ diff --git a/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzerTests.java b/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzerTests.java similarity index 64% rename from spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzerTests.java rename to spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzerTests.java index 24941f2c39f..036189f6339 100644 --- a/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionExceptionFailureAnalyzerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/NoUniqueBeanDefinitionFailureAnalyzerTests.java @@ -18,7 +18,8 @@ package org.springframework.boot.diagnostics.analyzer; import org.junit.Test; -import org.springframework.beans.factory.UnsatisfiedDependencyException; +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.diagnostics.analyzer.nounique.TestBean; @@ -32,21 +33,20 @@ import org.springframework.context.annotation.ImportResource; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link NoUniqueBeanDefinitionExceptionFailureAnalyzer}. + * Tests for {@link NoUniqueBeanDefinitionFailureAnalyzer}. * * @author Andy Wilkinson */ -public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { +public class NoUniqueBeanDefinitionFailureAnalyzerTests { - private final NoUniqueBeanDefinitionExceptionFailureAnalyzer analyzer = new NoUniqueBeanDefinitionExceptionFailureAnalyzer(); + private final NoUniqueBeanDefinitionFailureAnalyzer analyzer = new NoUniqueBeanDefinitionFailureAnalyzer(); @Test public void failureAnalysisForFieldConsumer() { FailureAnalysis failureAnalysis = analyzeFailure( createFailure(FieldConsumer.class)); - System.out.println(failureAnalysis.getDescription()); assertThat(failureAnalysis.getDescription()) - .startsWith("Field 'testBean' in " + FieldConsumer.class.getName() + .startsWith("Field testBean in " + FieldConsumer.class.getName() + " required a single bean, but 6 were found:"); assertFoundBeans(failureAnalysis); } @@ -55,9 +55,28 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { public void failureAnalysisForMethodConsumer() { FailureAnalysis failureAnalysis = analyzeFailure( createFailure(MethodConsumer.class)); - System.out.println(failureAnalysis.getDescription()); assertThat(failureAnalysis.getDescription()).startsWith( - "Parameter 0 of method 'consumer' in " + MethodConsumer.class.getName() + "Parameter 0 of method consumer in " + MethodConsumer.class.getName() + + " required a single bean, but 6 were found:"); + assertFoundBeans(failureAnalysis); + } + + @Test + public void failureAnalysisForConstructorConsumer() { + FailureAnalysis failureAnalysis = analyzeFailure( + createFailure(ConstructorConsumer.class)); + assertThat(failureAnalysis.getDescription()).startsWith( + "Parameter 0 of constructor in " + ConstructorConsumer.class.getName() + + " required a single bean, but 6 were found:"); + assertFoundBeans(failureAnalysis); + } + + @Test + public void failureAnalysisForObjectProviderMethodConsumer() { + FailureAnalysis failureAnalysis = analyzeFailure( + createFailure(ObjectProviderMethodConsumer.class)); + assertThat(failureAnalysis.getDescription()).startsWith( + "Method consumer in " + ObjectProviderMethodConsumer.class.getName() + " required a single bean, but 6 were found:"); assertFoundBeans(failureAnalysis); } @@ -66,14 +85,23 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { public void failureAnalysisForXmlConsumer() { FailureAnalysis failureAnalysis = analyzeFailure( createFailure(XmlConsumer.class)); - System.out.println(failureAnalysis.getDescription()); assertThat(failureAnalysis.getDescription()).startsWith( "Parameter 0 of constructor in " + TestBeanConsumer.class.getName() + " required a single bean, but 6 were found:"); assertFoundBeans(failureAnalysis); } - private UnsatisfiedDependencyException createFailure(Class consumer) { + @Test + public void failureAnalysisForObjectProviderConstructorConsumer() { + FailureAnalysis failureAnalysis = analyzeFailure( + createFailure(ObjectProviderConstructorConsumer.class)); + assertThat(failureAnalysis.getDescription()).startsWith( + "Constructor in " + ObjectProviderConstructorConsumer.class.getName() + + " required a single bean, but 6 were found:"); + assertFoundBeans(failureAnalysis); + } + + private BeanCreationException createFailure(Class consumer) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(DuplicateBeansProducer.class, consumer); context.setParent(new AnnotationConfigApplicationContext(ParentProducer.class)); @@ -81,7 +109,7 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { context.refresh(); return null; } - catch (UnsatisfiedDependencyException ex) { + catch (BeanCreationException ex) { this.analyzer.setBeanFactory(context.getBeanFactory()); return ex; } @@ -90,7 +118,7 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { } } - private FailureAnalysis analyzeFailure(UnsatisfiedDependencyException failure) { + private FailureAnalysis analyzeFailure(BeanCreationException failure) { return this.analyzer.analyze(failure); } @@ -144,6 +172,24 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { } + @Configuration + static class ObjectProviderConstructorConsumer { + + ObjectProviderConstructorConsumer(ObjectProvider objectProvider) { + objectProvider.getIfAvailable(); + } + + } + + @Configuration + static class ConstructorConsumer { + + ConstructorConsumer(TestBean testBean) { + + } + + } + @Configuration static class MethodConsumer { @@ -154,6 +200,17 @@ public class NoUniqueBeanDefinitionExceptionFailureAnalyzerTests { } + @Configuration + static class ObjectProviderMethodConsumer { + + @Bean + String consumer(ObjectProvider testBeanProvider) { + testBeanProvider.getIfAvailable(); + return "foo"; + } + + } + @Configuration @ImportResource("/org/springframework/boot/diagnostics/analyzer/nounique/consumer.xml") static class XmlConsumer {