Improve @PropertyMapping error message

Improve the message thrown when a @PropertyMapping is used in
combination with a @Component to include the actual annotations that
are causing the problem.

Fixes gh-5897
This commit is contained in:
Phillip Webb 2016-05-13 20:00:48 -07:00
parent 66b69f4346
commit cd365bcae1
2 changed files with 47 additions and 10 deletions

View File

@ -16,6 +16,10 @@
package org.springframework.boot.test.autoconfigure.properties;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
@ -24,7 +28,7 @@ import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link ContextCustomizer} to map annotation attributes to {@link Environment}
@ -72,17 +76,49 @@ class PropertyMappingContextCustomizer implements ContextCustomizer {
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
Class<?> beanClass = bean.getClass();
boolean hasComponent = AnnotationUtils.findAnnotation(beanClass,
Component.class) != null;
boolean hasPropertyMapping = AnnotationUtils.findAnnotation(beanClass,
PropertyMapping.class) != null;
if (hasComponent) {
Assert.state(!hasPropertyMapping,
"@PropertyMapping annotations can only be used on test classes");
Set<Class<?>> components = new LinkedHashSet<Class<?>>();
Set<Class<?>> propertyMappings = new LinkedHashSet<Class<?>>();
while (beanClass != null) {
for (Annotation annotation : AnnotationUtils.getAnnotations(beanClass)) {
if (isAnnotated(annotation, Component.class)) {
components.add(annotation.annotationType());
}
if (isAnnotated(annotation, PropertyMapping.class)) {
propertyMappings.add(annotation.annotationType());
}
}
beanClass = beanClass.getSuperclass();
}
if (!components.isEmpty() && !propertyMappings.isEmpty()) {
throw new IllegalStateException("The @PropertyMapping "
+ getAnnotationsDescription(propertyMappings)
+ " cannot be used in combination with the @Component "
+ getAnnotationsDescription(components));
}
return bean;
}
private boolean isAnnotated(Annotation element,
Class<? extends Annotation> annotationType) {
try {
return element.annotationType().equals(annotationType) || AnnotationUtils
.findAnnotation(element.annotationType(), annotationType) != null;
}
catch (Throwable ex) {
return false;
}
}
private String getAnnotationsDescription(Set<Class<?>> annotations) {
StringBuilder result = new StringBuilder();
for (Class<?> annotation : annotations) {
result.append(result.length() == 0 ? "" : ", ");
result.append("@" + ClassUtils.getShortName(annotation));
}
result.insert(0, annotations.size() == 1 ? "annotation " : "annotations ");
return result.toString();
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {

View File

@ -107,8 +107,9 @@ public class PropertyMappingContextCustomizerFactoryTests {
context.register(ConfigMapping.class);
customizer.customizeContext(context, null);
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage(
"@PropertyMapping annotations can only be used on test classes");
this.thrown.expectMessage("The @PropertyMapping annotation "
+ "@PropertyMappingContextCustomizerFactoryTests.TypeMappingAnnotation "
+ "cannot be used in combination with the @Component annotation @Configuration");
context.refresh();
}