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 a66c77cf1c8..05afe9e368d 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 @@ -128,10 +128,27 @@ public abstract class AnnotationUtils { } } + /** + * Get all {@link Annotation Annotations} from the supplied Method, Constructor or Field. + * @param annotatedElement the Method, Constructor or Field to retrieve annotations from + * @return the annotations found + * @since 4.0.8 + */ + public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { + try { + return annotatedElement.getAnnotations(); + } + catch (Exception ex) { + // Assuming nested Class values not resolvable within annotation attributes... + logIntrospectionFailure(annotatedElement, ex); + return null; + } + } + /** * Get all {@link Annotation Annotations} from the supplied {@link Method}. *

Correctly handles bridge {@link Method Methods} generated by the compiler. - * @param method the method to look for annotations on + * @param method the Method to retrieve annotations from * @return the annotations found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) */ diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java new file mode 100644 index 00000000000..2cc12717b2f --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java @@ -0,0 +1,95 @@ +/* + * Copyright 2002-2014 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 org.springframework.core.type.classreading; + +import java.lang.reflect.Field; + +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; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.util.ReflectionUtils; + +/** + * @author Chris Beams + * @author Juergen Hoeller + * @author Phillip Webb + * @author Sam Brannen + * @since 3.1.1 + */ +abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { + + protected final Log logger = LogFactory.getLog(getClass()); + + protected final AnnotationAttributes attributes; + + protected final ClassLoader classLoader; + + + public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) { + super(SpringAsmInfo.ASM_VERSION); + this.classLoader = classLoader; + this.attributes = attributes; + } + + + @Override + public void visit(String attributeName, Object attributeValue) { + this.attributes.put(attributeName, attributeValue); + } + + @Override + public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { + String annotationType = Type.getType(asmTypeDescriptor).getClassName(); + AnnotationAttributes nestedAttributes = new AnnotationAttributes(); + this.attributes.put(attributeName, nestedAttributes); + return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); + } + + @Override + public AnnotationVisitor visitArray(String attributeName) { + return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader); + } + + @Override + public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) { + Object newValue = getEnumValue(asmTypeDescriptor, attributeValue); + visit(attributeName, newValue); + } + + protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) { + Object valueToUse = attributeValue; + try { + Class enumType = this.classLoader.loadClass(Type.getType(asmTypeDescriptor).getClassName()); + Field enumConstant = ReflectionUtils.findField(enumType, attributeValue); + if (enumConstant != null) { + valueToUse = enumConstant.get(null); + } + } + catch (ClassNotFoundException ex) { + logger.debug("Failed to classload enum type while reading annotation metadata", ex); + } + catch (IllegalAccessException ex) { + logger.warn("Could not access enum value while reading annotation metadata", ex); + } + return valueToUse; + } + +} 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 337118a513e..4af65e9cf9e 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 @@ -17,211 +17,15 @@ package org.springframework.core.type.classreading; 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; import java.util.Map; 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; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.MultiValueMap; -import org.springframework.util.ObjectUtils; -import org.springframework.util.ReflectionUtils; - -/** - * @author Chris Beams - * @author Juergen Hoeller - * @author Phillip Webb - * @author Sam Brannen - * @since 3.1.1 - */ -abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { - - protected final Log logger = LogFactory.getLog(getClass()); - - protected final AnnotationAttributes attributes; - - protected final ClassLoader classLoader; - - public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) { - super(SpringAsmInfo.ASM_VERSION); - this.classLoader = classLoader; - this.attributes = attributes; - } - - @Override - public void visit(String attributeName, Object attributeValue) { - this.attributes.put(attributeName, attributeValue); - } - - @Override - public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { - String annotationType = Type.getType(asmTypeDescriptor).getClassName(); - AnnotationAttributes nestedAttributes = new AnnotationAttributes(); - this.attributes.put(attributeName, nestedAttributes); - return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); - } - - @Override - public AnnotationVisitor visitArray(String attributeName) { - return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader); - } - - @Override - public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) { - Object newValue = getEnumValue(asmTypeDescriptor, attributeValue); - visit(attributeName, newValue); - } - - protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) { - Object valueToUse = attributeValue; - try { - Class enumType = this.classLoader.loadClass(Type.getType(asmTypeDescriptor).getClassName()); - Field enumConstant = ReflectionUtils.findField(enumType, attributeValue); - if (enumConstant != null) { - valueToUse = enumConstant.get(null); - } - } - catch (ClassNotFoundException ex) { - logger.debug("Failed to classload enum type while reading annotation metadata", ex); - } - catch (IllegalAccessException ex) { - logger.warn("Could not access enum value while reading annotation metadata", ex); - } - return valueToUse; - } -} - - -/** - * @author Chris Beams - * @author Juergen Hoeller - * @since 3.1.1 - */ -final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor { - - private final String attributeName; - - private final List allNestedAttributes = new ArrayList(); - - public RecursiveAnnotationArrayVisitor( - String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) { - super(classLoader, attributes); - this.attributeName = attributeName; - } - - @Override - public void visit(String attributeName, Object attributeValue) { - Object newValue = attributeValue; - Object existingValue = this.attributes.get(this.attributeName); - if (existingValue != null) { - newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue); - } - else { - Class arrayClass = newValue.getClass(); - if (Enum.class.isAssignableFrom(arrayClass)) { - while (arrayClass.getSuperclass() != null && !arrayClass.isEnum()) { - arrayClass = arrayClass.getSuperclass(); - } - } - Object[] newArray = (Object[]) Array.newInstance(arrayClass, 1); - newArray[0] = newValue; - newValue = newArray; - } - this.attributes.put(this.attributeName, newValue); - } - - @Override - public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { - String annotationType = Type.getType(asmTypeDescriptor).getClassName(); - AnnotationAttributes nestedAttributes = new AnnotationAttributes(); - this.allNestedAttributes.add(nestedAttributes); - return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); - } - - @Override - public void visitEnd() { - if (!this.allNestedAttributes.isEmpty()) { - this.attributes.put(this.attributeName, - this.allNestedAttributes.toArray(new AnnotationAttributes[this.allNestedAttributes.size()])); - } - } -} - - -/** - * @author Chris Beams - * @author Juergen Hoeller - * @since 3.1.1 - */ -class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor { - - private final String annotationType; - - public RecursiveAnnotationAttributesVisitor(String annotationType, AnnotationAttributes attributes, - ClassLoader classLoader) { - super(classLoader, attributes); - this.annotationType = annotationType; - } - - @Override - public final void visitEnd() { - try { - Class annotationClass = this.classLoader.loadClass(this.annotationType); - doVisitEnd(annotationClass); - } - catch (ClassNotFoundException ex) { - 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); - } - } - - protected void doVisitEnd(Class annotationClass) { - registerDefaultValues(annotationClass); - } - - private void registerDefaultValues(Class annotationClass) { - // Only do further scanning for public annotations; we'd run into - // IllegalAccessExceptions otherwise, and we 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; - } - this.attributes.put(attributeName, defaultValue); - } - } - } - } -} - /** * ASM visitor which looks for the annotations defined on a class or method, including @@ -245,6 +49,7 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib private final Map> metaAnnotationMap; + public AnnotationAttributesReadingVisitor(String annotationType, MultiValueMap attributesMap, Map> metaAnnotationMap, ClassLoader classLoader) { @@ -255,6 +60,7 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib this.metaAnnotationMap = metaAnnotationMap; } + @Override public void doVisitEnd(Class annotationClass) { super.doVisitEnd(annotationClass); @@ -266,7 +72,7 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib attributes.add(0, this.attributes); } Set metaAnnotationTypeNames = new LinkedHashSet(); - for (Annotation metaAnnotation : annotationClass.getAnnotations()) { + for (Annotation metaAnnotation : AnnotationUtils.getAnnotations(annotationClass)) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { recursivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java index b0caadf5332..83a62aeebed 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/ClassMetadataReadingVisitor.java @@ -204,39 +204,38 @@ class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata return this.memberClassNames.toArray(new String[this.memberClassNames.size()]); } -} - -class EmptyAnnotationVisitor extends AnnotationVisitor { + private static class EmptyAnnotationVisitor extends AnnotationVisitor { - public EmptyAnnotationVisitor() { - super(SpringAsmInfo.ASM_VERSION); - } + public EmptyAnnotationVisitor() { + super(SpringAsmInfo.ASM_VERSION); + } - @Override - public AnnotationVisitor visitAnnotation(String name, String desc) { - return this; - } + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + return this; + } - @Override - public AnnotationVisitor visitArray(String name) { - return this; + @Override + public AnnotationVisitor visitArray(String name) { + return this; + } } -} -class EmptyMethodVisitor extends MethodVisitor { + private static class EmptyMethodVisitor extends MethodVisitor { - public EmptyMethodVisitor() { - super(SpringAsmInfo.ASM_VERSION); + public EmptyMethodVisitor() { + super(SpringAsmInfo.ASM_VERSION); + } } -} -class EmptyFieldVisitor extends FieldVisitor { + private static class EmptyFieldVisitor extends FieldVisitor { - public EmptyFieldVisitor() { - super(SpringAsmInfo.ASM_VERSION); + public EmptyFieldVisitor() { + super(SpringAsmInfo.ASM_VERSION); + } } -} \ No newline at end of file +} diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java new file mode 100644 index 00000000000..3c5bd9a43f6 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2014 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 org.springframework.core.type.classreading; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.asm.AnnotationVisitor; +import org.springframework.asm.Type; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.util.ObjectUtils; + +/** + * @author Chris Beams + * @author Juergen Hoeller + * @since 3.1.1 + */ +class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor { + + private final String attributeName; + + private final List allNestedAttributes = new ArrayList(); + + + public RecursiveAnnotationArrayVisitor( + String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) { + + super(classLoader, attributes); + this.attributeName = attributeName; + } + + + @Override + public void visit(String attributeName, Object attributeValue) { + Object newValue = attributeValue; + Object existingValue = this.attributes.get(this.attributeName); + if (existingValue != null) { + newValue = ObjectUtils.addObjectToArray((Object[]) existingValue, newValue); + } + else { + Class arrayClass = newValue.getClass(); + if (Enum.class.isAssignableFrom(arrayClass)) { + while (arrayClass.getSuperclass() != null && !arrayClass.isEnum()) { + arrayClass = arrayClass.getSuperclass(); + } + } + Object[] newArray = (Object[]) Array.newInstance(arrayClass, 1); + newArray[0] = newValue; + newValue = newArray; + } + this.attributes.put(this.attributeName, newValue); + } + + @Override + public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { + String annotationType = Type.getType(asmTypeDescriptor).getClassName(); + AnnotationAttributes nestedAttributes = new AnnotationAttributes(); + this.allNestedAttributes.add(nestedAttributes); + return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); + } + + @Override + public void visitEnd() { + if (!this.allNestedAttributes.isEmpty()) { + this.attributes.put(this.attributeName, + this.allNestedAttributes.toArray(new AnnotationAttributes[this.allNestedAttributes.size()])); + } + } + +} diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java new file mode 100644 index 00000000000..2f1be81db2c --- /dev/null +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2014 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 org.springframework.core.type.classreading; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationUtils; + +/** + * @author Chris Beams + * @author Juergen Hoeller + * @since 3.1.1 + */ +class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor { + + private final String annotationType; + + + public RecursiveAnnotationAttributesVisitor( + String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) { + + super(classLoader, attributes); + this.annotationType = annotationType; + } + + + @Override + public final void visitEnd() { + try { + Class annotationClass = this.classLoader.loadClass(this.annotationType); + doVisitEnd(annotationClass); + } + catch (ClassNotFoundException ex) { + 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); + } + } + + protected void doVisitEnd(Class annotationClass) { + registerDefaultValues(annotationClass); + } + + private void registerDefaultValues(Class annotationClass) { + // Only do further scanning for public annotations; we'd run into + // IllegalAccessExceptions otherwise, and we 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; + } + this.attributes.put(attributeName, defaultValue); + } + } + } + } + +}