From ec781650721c0217bd4c1cf2faa78cfb3c3043f2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 11 Dec 2013 18:42:48 +0100 Subject: [PATCH] Component scanning ignores attributes and meta-annotations on non-public annotations Issue: SPR-11091 (cherry picked from commit 161819f) --- .../example/scannable/CustomAnnotations.java | 34 ++++++++++ .../example/scannable/CustomComponent.java | 3 +- .../java/example/scannable/MessageBean.java | 4 +- .../AnnotationAttributesReadingVisitor.java | 66 +++++++++++-------- 4 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 spring-context/src/test/java/example/scannable/CustomAnnotations.java diff --git a/spring-context/src/test/java/example/scannable/CustomAnnotations.java b/spring-context/src/test/java/example/scannable/CustomAnnotations.java new file mode 100644 index 0000000000..dde136bc0f --- /dev/null +++ b/spring-context/src/test/java/example/scannable/CustomAnnotations.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package example.scannable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class CustomAnnotations { + + @Retention(RetentionPolicy.RUNTIME) + private @interface PrivateAnnotation { + String value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @PrivateAnnotation("special") + public @interface SpecialAnnotation { + } + +} diff --git a/spring-context/src/test/java/example/scannable/CustomComponent.java b/spring-context/src/test/java/example/scannable/CustomComponent.java index 2f6ef7f42a..0b26b32c18 100644 --- a/spring-context/src/test/java/example/scannable/CustomComponent.java +++ b/spring-context/src/test/java/example/scannable/CustomComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited +@CustomAnnotations.SpecialAnnotation public @interface CustomComponent { } diff --git a/spring-context/src/test/java/example/scannable/MessageBean.java b/spring-context/src/test/java/example/scannable/MessageBean.java index a4fc2d2614..66d9054126 100644 --- a/spring-context/src/test/java/example/scannable/MessageBean.java +++ b/spring-context/src/test/java/example/scannable/MessageBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ package example.scannable; * @author Mark Fisher */ @CustomComponent +@CustomAnnotations.SpecialAnnotation public class MessageBean { private String message; @@ -32,6 +33,7 @@ public class MessageBean { this.message = message; } + @CustomAnnotations.SpecialAnnotation public String getMessage() { return this.message; } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index 661ba30448..e09b81ae4c 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -20,6 +20,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -28,6 +29,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.SpringAsmInfo; import org.springframework.asm.Type; @@ -36,7 +38,6 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; - /** * @author Chris Beams * @author Juergen Hoeller @@ -169,12 +170,11 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi public final void visitEnd() { try { Class annotationClass = this.classLoader.loadClass(this.annotationType); - this.doVisitEnd(annotationClass); + doVisitEnd(annotationClass); } catch (ClassNotFoundException ex) { - this.logger.debug("Failed to classload type while reading annotation " + - "metadata. This is a non-fatal error, but certain annotation " + - "metadata may be unavailable.", ex); + this.logger.debug("Failed to class-load type while reading annotation metadata. " + + "This is a non-fatal error, but certain annotation metadata may be unavailable.", ex); } } @@ -183,26 +183,30 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi } private void registerDefaultValues(Class annotationClass) { - // Check declared default values of attributes in the annotation type. - Method[] annotationAttributes = annotationClass.getMethods(); - for (Method annotationAttribute : annotationAttributes) { - String attributeName = annotationAttribute.getName(); - Object defaultValue = annotationAttribute.getDefaultValue(); - if (defaultValue != null && !this.attributes.containsKey(attributeName)) { - if (defaultValue instanceof Annotation) { - defaultValue = AnnotationAttributes.fromMap( - AnnotationUtils.getAnnotationAttributes((Annotation)defaultValue, false, true)); - } - else if (defaultValue instanceof Annotation[]) { - Annotation[] realAnnotations = (Annotation[]) defaultValue; - AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; - for (int i = 0; i < realAnnotations.length; i++) { - mappedAnnotations[i] = AnnotationAttributes.fromMap( - AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false, true)); + // Only do further scanning for public annotations; we'd run into IllegalAccessExceptions + // otherwise, and don't want to mess with accessibility in a SecurityManager environment. + if (Modifier.isPublic(annotationClass.getModifiers())) { + // Check declared default values of attributes in the annotation type. + Method[] annotationAttributes = annotationClass.getMethods(); + for (Method annotationAttribute : annotationAttributes) { + String attributeName = annotationAttribute.getName(); + Object defaultValue = annotationAttribute.getDefaultValue(); + if (defaultValue != null && !this.attributes.containsKey(attributeName)) { + if (defaultValue instanceof Annotation) { + defaultValue = AnnotationAttributes.fromMap( + AnnotationUtils.getAnnotationAttributes((Annotation) defaultValue, false, true)); + } + else if (defaultValue instanceof Annotation[]) { + Annotation[] realAnnotations = (Annotation[]) defaultValue; + AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; + for (int i = 0; i < realAnnotations.length; i++) { + mappedAnnotations[i] = AnnotationAttributes.fromMap( + AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false, true)); + } + defaultValue = mappedAnnotations; } - defaultValue = mappedAnnotations; + this.attributes.put(attributeName, defaultValue); } - this.attributes.put(attributeName, defaultValue); } } } @@ -251,12 +255,16 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib Set metaAnnotationTypeNames = new LinkedHashSet(); for (Annotation metaAnnotation : annotationClass.getAnnotations()) { metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName()); - if (!this.attributesMap.containsKey(metaAnnotation.annotationType().getName())) { - this.attributesMap.put(metaAnnotation.annotationType().getName(), - AnnotationUtils.getAnnotationAttributes(metaAnnotation, true, true)); - } - for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) { - metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName()); + // Only do further scanning for public annotations; we'd run into IllegalAccessExceptions + // otherwise, and don't want to mess with accessibility in a SecurityManager environment. + if (Modifier.isPublic(metaAnnotation.annotationType().getModifiers())) { + if (!this.attributesMap.containsKey(metaAnnotation.annotationType().getName())) { + this.attributesMap.put(metaAnnotation.annotationType().getName(), + AnnotationUtils.getAnnotationAttributes(metaAnnotation, true, true)); + } + for (Annotation metaMetaAnnotation : metaAnnotation.annotationType().getAnnotations()) { + metaAnnotationTypeNames.add(metaMetaAnnotation.annotationType().getName()); + } } } if (this.metaAnnotationMap != null) { -- GitLab