AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well
Issue: SPR-12065
This commit is contained in:
@@ -28,6 +28,7 @@ import org.junit.Test;
|
|||||||
import org.springframework.aop.scope.ScopedObject;
|
import org.springframework.aop.scope.ScopedObject;
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
@@ -39,6 +40,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
|||||||
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
|
||||||
import org.springframework.core.env.StandardEnvironment;
|
import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.core.io.DescriptiveResource;
|
import org.springframework.core.io.DescriptiveResource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.tests.sample.beans.ITestBean;
|
import org.springframework.tests.sample.beans.ITestBean;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@@ -126,19 +128,33 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingReflection() {
|
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() {
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
ComposedConfigurationWithAttributeOverridesClass.class);
|
ComposedConfigurationWithAttributeOverrideForBasePackage.class);
|
||||||
assertSupportForComposedAnnotation(beanDefinition);
|
assertSupportForComposedAnnotation(beanDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingAsm() {
|
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() {
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
ComposedConfigurationWithAttributeOverridesClass.class.getName());
|
ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName());
|
||||||
assertSupportForComposedAnnotation(beanDefinition);
|
assertSupportForComposedAnnotation(beanDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() {
|
||||||
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
|
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class);
|
||||||
|
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() {
|
||||||
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
|
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class.getName());
|
||||||
|
assertSupportForComposedAnnotationWithExclude(beanDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
|
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
|
||||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(
|
||||||
@@ -181,6 +197,29 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
assertSupportForComposedAnnotation(beanDefinition);
|
assertSupportForComposedAnnotation(beanDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
|
||||||
|
beanFactory.registerBeanDefinition("config", beanDefinition);
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.setEnvironment(new StandardEnvironment());
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
|
||||||
|
assertNotNull(simpleComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSupportForComposedAnnotationWithExclude(RootBeanDefinition beanDefinition) {
|
||||||
|
beanFactory.registerBeanDefinition("config", beanDefinition);
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.setEnvironment(new StandardEnvironment());
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
try {
|
||||||
|
beanFactory.getBean(SimpleComponent.class);
|
||||||
|
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void postProcessorOverridesNonApplicationBeanDefinitions() {
|
public void postProcessorOverridesNonApplicationBeanDefinitions() {
|
||||||
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
|
||||||
@@ -378,15 +417,6 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
|
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
|
|
||||||
beanFactory.registerBeanDefinition("config", beanDefinition);
|
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
|
||||||
pp.setEnvironment(new StandardEnvironment());
|
|
||||||
pp.postProcessBeanFactory(beanFactory);
|
|
||||||
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
|
|
||||||
assertNotNull(simpleComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
public void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
@@ -718,10 +748,17 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
public static @interface ComposedConfigurationWithAttributeOverrides {
|
public static @interface ComposedConfigurationWithAttributeOverrides {
|
||||||
|
|
||||||
String[] basePackages() default {};
|
String[] basePackages() default {};
|
||||||
|
|
||||||
|
ComponentScan.Filter[] excludeFilters() default {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple")
|
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple")
|
||||||
public static class ComposedConfigurationWithAttributeOverridesClass {
|
public static class ComposedConfigurationWithAttributeOverrideForBasePackage {
|
||||||
|
}
|
||||||
|
|
||||||
|
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple",
|
||||||
|
excludeFilters = @ComponentScan.Filter(Component.class))
|
||||||
|
public static class ComposedConfigurationWithAttributeOverrideForExcludeFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ComposedConfigurationWithAttributeOverrides
|
@ComposedConfigurationWithAttributeOverrides
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public class AnnotatedElementUtils {
|
|||||||
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
|
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
|
||||||
final Set<String> types = new LinkedHashSet<String>();
|
final Set<String> types = new LinkedHashSet<String>();
|
||||||
process(element, annotationType, false, new Processor<Object>() {
|
process(element, annotationType, false, new Processor<Object>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object process(Annotation annotation, int metaDepth) {
|
public Object process(Annotation annotation, int metaDepth) {
|
||||||
if (metaDepth > 0) {
|
if (metaDepth > 0) {
|
||||||
@@ -48,7 +47,6 @@ public class AnnotatedElementUtils {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcess(Annotation annotation, Object result) {
|
public void postProcess(Annotation annotation, Object result) {
|
||||||
}
|
}
|
||||||
@@ -101,7 +99,7 @@ public class AnnotatedElementUtils {
|
|||||||
if (!AnnotationUtils.VALUE.equals(key)) {
|
if (!AnnotationUtils.VALUE.equals(key)) {
|
||||||
Object value = AnnotationUtils.getValue(annotation, key);
|
Object value = AnnotationUtils.getValue(annotation, key);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
result.put(key, value);
|
result.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,8 +107,7 @@ public class AnnotatedElementUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
|
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
|
||||||
String annotationType) {
|
|
||||||
return getAllAnnotationAttributes(element, annotationType, false, false);
|
return getAllAnnotationAttributes(element, annotationType, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,8 +119,8 @@ public class AnnotatedElementUtils {
|
|||||||
@Override
|
@Override
|
||||||
public Void process(Annotation annotation, int metaDepth) {
|
public Void process(Annotation annotation, int metaDepth) {
|
||||||
if (annotation.annotationType().getName().equals(annotationType)) {
|
if (annotation.annotationType().getName().equals(annotationType)) {
|
||||||
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(annotation,
|
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
|
||||||
classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
|
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
|
||||||
attributes.add(entry.getKey(), entry.getValue());
|
attributes.add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +160,7 @@ public class AnnotatedElementUtils {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return doProcess(element, annotationType, traverseClassHierarchy, processor,
|
return doProcess(element, annotationType, traverseClassHierarchy, processor,
|
||||||
new HashSet<AnnotatedElement>(), 0);
|
new HashSet<AnnotatedElement>(), 0);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
|
throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
|
||||||
@@ -199,8 +196,8 @@ public class AnnotatedElementUtils {
|
|||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, processor,
|
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
|
||||||
visited, metaDepth + 1);
|
processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
processor.postProcess(annotation, result);
|
processor.postProcess(annotation, result);
|
||||||
return result;
|
return result;
|
||||||
@@ -210,7 +207,7 @@ public class AnnotatedElementUtils {
|
|||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||||
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
|
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
|
||||||
processor, visited, metaDepth);
|
processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
processor.postProcess(annotation, result);
|
processor.postProcess(annotation, result);
|
||||||
return result;
|
return result;
|
||||||
@@ -220,8 +217,7 @@ public class AnnotatedElementUtils {
|
|||||||
if (traverseClassHierarchy && element instanceof Class) {
|
if (traverseClassHierarchy && element instanceof Class) {
|
||||||
Class<?> superclass = ((Class<?>) element).getSuperclass();
|
Class<?> superclass = ((Class<?>) element).getSuperclass();
|
||||||
if (superclass != null && !superclass.equals(Object.class)) {
|
if (superclass != null && !superclass.equals(Object.class)) {
|
||||||
T result = doProcess(superclass, annotationType, true, processor, visited,
|
T result = doProcess(superclass, annotationType, true, processor, visited, metaDepth);
|
||||||
metaDepth);
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -247,7 +243,7 @@ public class AnnotatedElementUtils {
|
|||||||
* will have a depth of 2.
|
* will have a depth of 2.
|
||||||
* @param annotation the annotation to process
|
* @param annotation the annotation to process
|
||||||
* @param metaDepth the depth of the annotation relative to the initial element
|
* @param metaDepth the depth of the annotation relative to the initial element
|
||||||
* @return the result of the processing or {@code null} to continue
|
* @return the result of the processing, or {@code null} to continue
|
||||||
*/
|
*/
|
||||||
T process(Annotation annotation, int metaDepth);
|
T process(Annotation annotation, int metaDepth);
|
||||||
|
|
||||||
|
|||||||
@@ -578,34 +578,7 @@ public abstract class AnnotationUtils {
|
|||||||
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
|
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
|
||||||
try {
|
try {
|
||||||
Object value = method.invoke(annotation);
|
Object value = method.invoke(annotation);
|
||||||
if (classValuesAsString) {
|
attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
|
||||||
if (value instanceof Class) {
|
|
||||||
value = ((Class<?>) value).getName();
|
|
||||||
}
|
|
||||||
else if (value instanceof Class[]) {
|
|
||||||
Class<?>[] clazzArray = (Class[]) value;
|
|
||||||
String[] newValue = new String[clazzArray.length];
|
|
||||||
for (int i = 0; i < clazzArray.length; i++) {
|
|
||||||
newValue[i] = clazzArray[i].getName();
|
|
||||||
}
|
|
||||||
value = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nestedAnnotationsAsMap && value instanceof Annotation) {
|
|
||||||
attrs.put(method.getName(),
|
|
||||||
getAnnotationAttributes((Annotation) value, classValuesAsString, true));
|
|
||||||
}
|
|
||||||
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
|
|
||||||
Annotation[] realAnnotations = (Annotation[]) value;
|
|
||||||
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
|
||||||
for (int i = 0; i < realAnnotations.length; i++) {
|
|
||||||
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
|
|
||||||
}
|
|
||||||
attrs.put(method.getName(), mappedAnnotations);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
attrs.put(method.getName(), value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
|
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
|
||||||
@@ -615,6 +588,48 @@ public abstract class AnnotationUtils {
|
|||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapt the given value according to the given class and nested annotation settings.
|
||||||
|
* @param value the annotation attribute value
|
||||||
|
* @param classValuesAsString whether to turn Class references into Strings (for
|
||||||
|
* compatibility with {@link org.springframework.core.type.AnnotationMetadata}
|
||||||
|
* or to preserve them as Class references
|
||||||
|
* @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
|
||||||
|
* {@link AnnotationAttributes} maps (for compatibility with
|
||||||
|
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
|
||||||
|
* Annotation instances
|
||||||
|
* @return the adapted value, or the original value if no adaptation is needed
|
||||||
|
*/
|
||||||
|
static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
|
||||||
|
if (classValuesAsString) {
|
||||||
|
if (value instanceof Class) {
|
||||||
|
value = ((Class<?>) value).getName();
|
||||||
|
}
|
||||||
|
else if (value instanceof Class[]) {
|
||||||
|
Class<?>[] clazzArray = (Class[]) value;
|
||||||
|
String[] newValue = new String[clazzArray.length];
|
||||||
|
for (int i = 0; i < clazzArray.length; i++) {
|
||||||
|
newValue[i] = clazzArray[i].getName();
|
||||||
|
}
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nestedAnnotationsAsMap && value instanceof Annotation) {
|
||||||
|
return getAnnotationAttributes((Annotation) value, classValuesAsString, true);
|
||||||
|
}
|
||||||
|
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
|
||||||
|
Annotation[] realAnnotations = (Annotation[]) value;
|
||||||
|
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
|
||||||
|
for (int i = 0; i < realAnnotations.length; i++) {
|
||||||
|
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
|
||||||
|
}
|
||||||
|
return mappedAnnotations;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the <em>value</em> of the {@code "value"} attribute of a
|
* Retrieve the <em>value</em> of the {@code "value"} attribute of a
|
||||||
* single-element Annotation, given an annotation instance.
|
* single-element Annotation, given an annotation instance.
|
||||||
|
|||||||
Reference in New Issue
Block a user