From d61353db513c7b1ea367b3ff4c429042566f1003 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Aug 2014 15:15:29 +0200 Subject: [PATCH] AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well Issue: SPR-12065 (cherry picked from commit ef51d4d) --- .../ConfigurationClassPostProcessorTests.java | 65 +++++++++++++---- .../annotation/AnnotatedElementUtils.java | 24 +++---- .../core/annotation/AnnotationUtils.java | 71 +++++++++++-------- 3 files changed, 104 insertions(+), 56 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 278f0499b7b..1e502ac187d 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -28,6 +28,7 @@ import org.junit.Test; import org.springframework.aop.scope.ScopedObject; import org.springframework.aop.scope.ScopedProxyUtils; 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.AutowiredAnnotationBeanPostProcessor; 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.core.env.StandardEnvironment; import org.springframework.core.io.DescriptiveResource; +import org.springframework.stereotype.Component; import org.springframework.tests.sample.beans.ITestBean; import org.springframework.tests.sample.beans.TestBean; import org.springframework.util.Assert; @@ -126,19 +128,33 @@ public class ConfigurationClassPostProcessorTests { } @Test - public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingReflection() { + public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( - ComposedConfigurationWithAttributeOverridesClass.class); + ComposedConfigurationWithAttributeOverrideForBasePackage.class); assertSupportForComposedAnnotation(beanDefinition); } @Test - public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingAsm() { + public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() { RootBeanDefinition beanDefinition = new RootBeanDefinition( - ComposedConfigurationWithAttributeOverridesClass.class.getName()); + ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName()); 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 public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() { RootBeanDefinition beanDefinition = new RootBeanDefinition( @@ -181,6 +197,29 @@ public class ConfigurationClassPostProcessorTests { 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 public void postProcessorOverridesNonApplicationBeanDefinitions() { RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); @@ -378,15 +417,6 @@ public class ConfigurationClassPostProcessorTests { 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 public void testSelfReferenceExclusionForFactoryMethodOnSameBean() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -718,10 +748,17 @@ public class ConfigurationClassPostProcessorTests { public static @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; + + ComponentScan.Filter[] excludeFilters() default {}; } @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 diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index aa9bdff028c..af84f7b5256 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -40,7 +40,6 @@ public class AnnotatedElementUtils { public static Set getMetaAnnotationTypes(AnnotatedElement element, String annotationType) { final Set types = new LinkedHashSet(); process(element, annotationType, false, new Processor() { - @Override public Object process(Annotation annotation, int metaDepth) { if (metaDepth > 0) { @@ -48,7 +47,6 @@ public class AnnotatedElementUtils { } return null; } - @Override public void postProcess(Annotation annotation, Object result) { } @@ -101,7 +99,7 @@ public class AnnotatedElementUtils { if (!AnnotationUtils.VALUE.equals(key)) { Object value = AnnotationUtils.getValue(annotation, key); 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 getAllAnnotationAttributes(AnnotatedElement element, - String annotationType) { + public static MultiValueMap getAllAnnotationAttributes(AnnotatedElement element, String annotationType) { return getAllAnnotationAttributes(element, annotationType, false, false); } @@ -122,8 +119,8 @@ public class AnnotatedElementUtils { @Override public Void process(Annotation annotation, int metaDepth) { if (annotation.annotationType().getName().equals(annotationType)) { - for (Map.Entry entry : AnnotationUtils.getAnnotationAttributes(annotation, - classValuesAsString, nestedAnnotationsAsMap).entrySet()) { + for (Map.Entry entry : AnnotationUtils.getAnnotationAttributes( + annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) { attributes.add(entry.getKey(), entry.getValue()); } } @@ -163,7 +160,7 @@ public class AnnotatedElementUtils { try { return doProcess(element, annotationType, traverseClassHierarchy, processor, - new HashSet(), 0); + new HashSet(), 0); } catch (Throwable ex) { throw new IllegalStateException("Failed to introspect annotations: " + element, ex); @@ -199,8 +196,8 @@ public class AnnotatedElementUtils { if (result != null) { return result; } - result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, processor, - visited, metaDepth + 1); + result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, + processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(annotation, result); return result; @@ -210,7 +207,7 @@ public class AnnotatedElementUtils { for (Annotation annotation : annotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) { T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, - processor, visited, metaDepth); + processor, visited, metaDepth); if (result != null) { processor.postProcess(annotation, result); return result; @@ -220,8 +217,7 @@ public class AnnotatedElementUtils { if (traverseClassHierarchy && element instanceof Class) { Class superclass = ((Class) element).getSuperclass(); if (superclass != null && !superclass.equals(Object.class)) { - T result = doProcess(superclass, annotationType, true, processor, visited, - metaDepth); + T result = doProcess(superclass, annotationType, true, processor, visited, metaDepth); if (result != null) { return result; } @@ -247,7 +243,7 @@ public class AnnotatedElementUtils { * will have a depth of 2. * @param annotation the annotation to process * @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); diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 556317811e9..5e276fd5ee5 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -559,34 +559,7 @@ public abstract class AnnotationUtils { if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) { try { Object value = method.invoke(annotation); - 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) { - 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); - } + attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap)); } catch (Exception ex) { throw new IllegalStateException("Could not obtain annotation attribute values", ex); @@ -596,6 +569,48 @@ public abstract class AnnotationUtils { 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 value of the {@code "value"} attribute of a * single-element Annotation, given an annotation instance.