Skip to content

Commit ef51d4d

Browse files
committed
AnnotatedElementUtils adapts post-processed values to AnnotationAttributes as well
Issue: SPR-12065
1 parent a635c36 commit ef51d4d

File tree

3 files changed

+104
-56
lines changed

3 files changed

+104
-56
lines changed

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.aop.scope.ScopedObject;
2929
import org.springframework.aop.scope.ScopedProxyUtils;
3030
import org.springframework.beans.factory.FactoryBean;
31+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3132
import org.springframework.beans.factory.annotation.Autowired;
3233
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
3334
import org.springframework.beans.factory.annotation.Qualifier;
@@ -39,6 +40,7 @@
3940
import org.springframework.context.annotation.componentscan.simple.SimpleComponent;
4041
import org.springframework.core.env.StandardEnvironment;
4142
import org.springframework.core.io.DescriptiveResource;
43+
import org.springframework.stereotype.Component;
4244
import org.springframework.tests.sample.beans.ITestBean;
4345
import org.springframework.tests.sample.beans.TestBean;
4446
import org.springframework.util.Assert;
@@ -126,19 +128,33 @@ public void postProcessorWorksWithComposedConfigurationUsingAsm() {
126128
}
127129

128130
@Test
129-
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingReflection() {
131+
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingReflection() {
130132
RootBeanDefinition beanDefinition = new RootBeanDefinition(
131-
ComposedConfigurationWithAttributeOverridesClass.class);
133+
ComposedConfigurationWithAttributeOverrideForBasePackage.class);
132134
assertSupportForComposedAnnotation(beanDefinition);
133135
}
134136

135137
@Test
136-
public void postProcessorWorksWithComposedConfigurationWithAttributeOverridesUsingAsm() {
138+
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForBasePackageUsingAsm() {
137139
RootBeanDefinition beanDefinition = new RootBeanDefinition(
138-
ComposedConfigurationWithAttributeOverridesClass.class.getName());
140+
ComposedConfigurationWithAttributeOverrideForBasePackage.class.getName());
139141
assertSupportForComposedAnnotation(beanDefinition);
140142
}
141143

144+
@Test
145+
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingReflection() {
146+
RootBeanDefinition beanDefinition = new RootBeanDefinition(
147+
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class);
148+
assertSupportForComposedAnnotationWithExclude(beanDefinition);
149+
}
150+
151+
@Test
152+
public void postProcessorWorksWithComposedConfigurationWithAttributeOverrideForExcludeFilterUsingAsm() {
153+
RootBeanDefinition beanDefinition = new RootBeanDefinition(
154+
ComposedConfigurationWithAttributeOverrideForExcludeFilter.class.getName());
155+
assertSupportForComposedAnnotationWithExclude(beanDefinition);
156+
}
157+
142158
@Test
143159
public void postProcessorWorksWithComposedComposedConfigurationWithAttributeOverridesUsingReflection() {
144160
RootBeanDefinition beanDefinition = new RootBeanDefinition(
@@ -181,6 +197,29 @@ public void postProcessorWorksWithMetaComponentScanConfigurationWithAttributeOve
181197
assertSupportForComposedAnnotation(beanDefinition);
182198
}
183199

200+
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
201+
beanFactory.registerBeanDefinition("config", beanDefinition);
202+
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
203+
pp.setEnvironment(new StandardEnvironment());
204+
pp.postProcessBeanFactory(beanFactory);
205+
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
206+
assertNotNull(simpleComponent);
207+
}
208+
209+
private void assertSupportForComposedAnnotationWithExclude(RootBeanDefinition beanDefinition) {
210+
beanFactory.registerBeanDefinition("config", beanDefinition);
211+
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
212+
pp.setEnvironment(new StandardEnvironment());
213+
pp.postProcessBeanFactory(beanFactory);
214+
try {
215+
beanFactory.getBean(SimpleComponent.class);
216+
fail("Should have thrown NoSuchBeanDefinitionException");
217+
}
218+
catch (NoSuchBeanDefinitionException ex) {
219+
// expected
220+
}
221+
}
222+
184223
@Test
185224
public void postProcessorOverridesNonApplicationBeanDefinitions() {
186225
RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class);
@@ -378,15 +417,6 @@ public void genericsBasedInjectionWithWildcardWithGenericExtendsMatch() {
378417
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
379418
}
380419

381-
private void assertSupportForComposedAnnotation(RootBeanDefinition beanDefinition) {
382-
beanFactory.registerBeanDefinition("config", beanDefinition);
383-
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
384-
pp.setEnvironment(new StandardEnvironment());
385-
pp.postProcessBeanFactory(beanFactory);
386-
SimpleComponent simpleComponent = beanFactory.getBean(SimpleComponent.class);
387-
assertNotNull(simpleComponent);
388-
}
389-
390420
@Test
391421
public void testSelfReferenceExclusionForFactoryMethodOnSameBean() {
392422
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -718,10 +748,17 @@ public static class ComposedConfigurationClass {
718748
public static @interface ComposedConfigurationWithAttributeOverrides {
719749

720750
String[] basePackages() default {};
751+
752+
ComponentScan.Filter[] excludeFilters() default {};
721753
}
722754

723755
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple")
724-
public static class ComposedConfigurationWithAttributeOverridesClass {
756+
public static class ComposedConfigurationWithAttributeOverrideForBasePackage {
757+
}
758+
759+
@ComposedConfigurationWithAttributeOverrides(basePackages = "org.springframework.context.annotation.componentscan.simple",
760+
excludeFilters = @ComponentScan.Filter(Component.class))
761+
public static class ComposedConfigurationWithAttributeOverrideForExcludeFilter {
725762
}
726763

727764
@ComposedConfigurationWithAttributeOverrides

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,13 @@ public class AnnotatedElementUtils {
4040
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
4141
final Set<String> types = new LinkedHashSet<String>();
4242
process(element, annotationType, false, new Processor<Object>() {
43-
4443
@Override
4544
public Object process(Annotation annotation, int metaDepth) {
4645
if (metaDepth > 0) {
4746
types.add(annotation.annotationType().getName());
4847
}
4948
return null;
5049
}
51-
5250
@Override
5351
public void postProcess(Annotation annotation, Object result) {
5452
}
@@ -101,16 +99,15 @@ public void postProcess(Annotation annotation, AnnotationAttributes result) {
10199
if (!AnnotationUtils.VALUE.equals(key)) {
102100
Object value = AnnotationUtils.getValue(annotation, key);
103101
if (value != null) {
104-
result.put(key, value);
102+
result.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
105103
}
106104
}
107105
}
108106
}
109107
});
110108
}
111109

112-
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
113-
String annotationType) {
110+
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
114111
return getAllAnnotationAttributes(element, annotationType, false, false);
115112
}
116113

@@ -122,8 +119,8 @@ public static MultiValueMap<String, Object> getAllAnnotationAttributes(Annotated
122119
@Override
123120
public Void process(Annotation annotation, int metaDepth) {
124121
if (annotation.annotationType().getName().equals(annotationType)) {
125-
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(annotation,
126-
classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
122+
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
123+
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
127124
attributes.add(entry.getKey(), entry.getValue());
128125
}
129126
}
@@ -163,7 +160,7 @@ private static <T> T process(AnnotatedElement element, String annotationType, bo
163160

164161
try {
165162
return doProcess(element, annotationType, traverseClassHierarchy, processor,
166-
new HashSet<AnnotatedElement>(), 0);
163+
new HashSet<AnnotatedElement>(), 0);
167164
}
168165
catch (Throwable ex) {
169166
throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
@@ -199,8 +196,8 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
199196
if (result != null) {
200197
return result;
201198
}
202-
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, processor,
203-
visited, metaDepth + 1);
199+
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
200+
processor, visited, metaDepth + 1);
204201
if (result != null) {
205202
processor.postProcess(annotation, result);
206203
return result;
@@ -210,7 +207,7 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
210207
for (Annotation annotation : annotations) {
211208
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
212209
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
213-
processor, visited, metaDepth);
210+
processor, visited, metaDepth);
214211
if (result != null) {
215212
processor.postProcess(annotation, result);
216213
return result;
@@ -220,8 +217,7 @@ private static <T> T doProcess(AnnotatedElement element, String annotationType,
220217
if (traverseClassHierarchy && element instanceof Class) {
221218
Class<?> superclass = ((Class<?>) element).getSuperclass();
222219
if (superclass != null && !superclass.equals(Object.class)) {
223-
T result = doProcess(superclass, annotationType, true, processor, visited,
224-
metaDepth);
220+
T result = doProcess(superclass, annotationType, true, processor, visited, metaDepth);
225221
if (result != null) {
226222
return result;
227223
}
@@ -247,7 +243,7 @@ private static interface Processor<T> {
247243
* will have a depth of 2.
248244
* @param annotation the annotation to process
249245
* @param metaDepth the depth of the annotation relative to the initial element
250-
* @return the result of the processing or {@code null} to continue
246+
* @return the result of the processing, or {@code null} to continue
251247
*/
252248
T process(Annotation annotation, int metaDepth);
253249

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -578,34 +578,7 @@ public static AnnotationAttributes getAnnotationAttributes(Annotation annotation
578578
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
579579
try {
580580
Object value = method.invoke(annotation);
581-
if (classValuesAsString) {
582-
if (value instanceof Class) {
583-
value = ((Class<?>) value).getName();
584-
}
585-
else if (value instanceof Class[]) {
586-
Class<?>[] clazzArray = (Class[]) value;
587-
String[] newValue = new String[clazzArray.length];
588-
for (int i = 0; i < clazzArray.length; i++) {
589-
newValue[i] = clazzArray[i].getName();
590-
}
591-
value = newValue;
592-
}
593-
}
594-
if (nestedAnnotationsAsMap && value instanceof Annotation) {
595-
attrs.put(method.getName(),
596-
getAnnotationAttributes((Annotation) value, classValuesAsString, true));
597-
}
598-
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
599-
Annotation[] realAnnotations = (Annotation[]) value;
600-
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
601-
for (int i = 0; i < realAnnotations.length; i++) {
602-
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
603-
}
604-
attrs.put(method.getName(), mappedAnnotations);
605-
}
606-
else {
607-
attrs.put(method.getName(), value);
608-
}
581+
attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
609582
}
610583
catch (Exception ex) {
611584
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
@@ -615,6 +588,48 @@ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
615588
return attrs;
616589
}
617590

591+
/**
592+
* Adapt the given value according to the given class and nested annotation settings.
593+
* @param value the annotation attribute value
594+
* @param classValuesAsString whether to turn Class references into Strings (for
595+
* compatibility with {@link org.springframework.core.type.AnnotationMetadata}
596+
* or to preserve them as Class references
597+
* @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
598+
* {@link AnnotationAttributes} maps (for compatibility with
599+
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
600+
* Annotation instances
601+
* @return the adapted value, or the original value if no adaptation is needed
602+
*/
603+
static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
604+
if (classValuesAsString) {
605+
if (value instanceof Class) {
606+
value = ((Class<?>) value).getName();
607+
}
608+
else if (value instanceof Class[]) {
609+
Class<?>[] clazzArray = (Class[]) value;
610+
String[] newValue = new String[clazzArray.length];
611+
for (int i = 0; i < clazzArray.length; i++) {
612+
newValue[i] = clazzArray[i].getName();
613+
}
614+
value = newValue;
615+
}
616+
}
617+
if (nestedAnnotationsAsMap && value instanceof Annotation) {
618+
return getAnnotationAttributes((Annotation) value, classValuesAsString, true);
619+
}
620+
else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
621+
Annotation[] realAnnotations = (Annotation[]) value;
622+
AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
623+
for (int i = 0; i < realAnnotations.length; i++) {
624+
mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
625+
}
626+
return mappedAnnotations;
627+
}
628+
else {
629+
return value;
630+
}
631+
}
632+
618633
/**
619634
* Retrieve the <em>value</em> of the {@code &quot;value&quot;} attribute of a
620635
* single-element Annotation, given an annotation instance.

0 commit comments

Comments
 (0)