Extend AnnotationMetadata and MethodMetadata

Update AnnotationMetadata and MethodMetadata to extend from a new
AnnotatedTypeMetadata base interface containing the methods that are
common to both. Also introduce new getAllAnnotationAttributes methods
providing MultiValueMap access to both annotation and meta-annotation
attributes.

Existing classreading and standard implementations have been
refactored to support the new interface.
This commit is contained in:
Phillip Webb 2013-02-20 09:38:43 -08:00
parent eb1776e79d
commit 8e445f3a21
11 changed files with 537 additions and 247 deletions

View File

@ -0,0 +1,160 @@
/*
* 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 org.springframework.core.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Internal utility class used to collect all annotation values including those declared
* on meta-annotations.
*
* @author Phillip Webb
* @since 4.0
*/
class AnnotatedElementUtils {
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
String annotationType) {
final Set<String> types = new LinkedHashSet<String>();
process(element, annotationType, new Processor<Object>() {
public Object process(Annotation annotation, int depth) {
if (depth > 0) {
types.add(annotation.annotationType().getName());
}
return null;
}
});
return (types.size() == 0 ? null : types);
}
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
String annotationType) {
return Boolean.TRUE.equals(
process(element, annotationType, new Processor<Boolean>() {
public Boolean process(Annotation annotation, int depth) {
if (depth > 0) {
return true;
}
return null;
}
}));
}
public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
return Boolean.TRUE.equals(
process(element, annotationType, new Processor<Boolean>() {
public Boolean process(Annotation annotation, int depth) {
return true;
}
}));
}
public static Map<String, Object> getAnnotationAttributes(AnnotatedElement element,
String annotationType, final boolean classValuesAsString,
final boolean nestedAnnotationsAsMap) {
return process(element, annotationType, new Processor<Map<String, Object>>() {
public Map<String, Object> process(Annotation annotation, int depth) {
return AnnotationUtils.getAnnotationAttributes(annotation,
classValuesAsString, nestedAnnotationsAsMap);
}
});
}
public static MultiValueMap<String, Object> getAllAnnotationAttributes(
AnnotatedElement element, final String annotationType,
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
process(element, annotationType, new Processor<Object>() {
public Object process(Annotation annotation, int depth) {
if (annotation.annotationType().getName().equals(annotationType)) {
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
attributes.add(entry.getKey(), entry.getValue());
}
}
return null;
}
});
return (attributes.size() == 0 ? null : attributes);
}
/**
* Process all annotations of the specified annotation type and recursively all
* meta-annotations on the specified type.
* @param element the annotated element
* @param annotationType the annotation type to find. Only items of the specified type
* or meta-annotations of the specified type will be processed
* @param processor the processor
* @return the result of the processor
*/
private static <T> T process(AnnotatedElement element, String annotationType,
Processor<T> processor) {
return recursivelyProcess(element, annotationType, processor,
new HashSet<AnnotatedElement>(), 0);
}
private static <T> T recursivelyProcess(AnnotatedElement element,
String annotationType, Processor<T> processor, Set<AnnotatedElement> visited,
int depth) {
T result = null;
if (visited.add(element)) {
for (Annotation annotation : element.getAnnotations()) {
if (annotation.annotationType().getName().equals(annotationType) || depth > 0) {
result = result != null ? result :
processor.process(annotation, depth);
result = result != null ? result :
recursivelyProcess(annotation.annotationType(), annotationType,
processor, visited, depth + 1);
}
}
for (Annotation annotation : element.getAnnotations()) {
result = result != null ? result :
recursivelyProcess(annotation.annotationType(), annotationType,
processor, visited, depth);
}
}
return result;
}
/**
* Callback interface used to process an annotation.
* @param <T> the result type
*/
private static interface Processor<T> {
/**
* Called to process the annotation.
* @param annotation the annotation to process
* @param depth the depth of the annotation relative to the initial match. For
* example a matched annotation will have a depth of 0, a meta-annotation 1
* and a meta-meta-annotation 2
* @return the result of the processing or {@code null} to continue
*/
T process(Annotation annotation, int depth);
}
}

View File

@ -0,0 +1,97 @@
/*
* 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 org.springframework.core.type;
import java.util.Map;
import org.springframework.util.MultiValueMap;
/**
* Defines access to the annotations of a specific type ({@link AnnotationMetadata class}
* or {@link MethodMetadata method}), in a form that does not necessarily require the
* class-loading.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Mark Pollack
* @author Chris Beams
* @author Phillip Webb
* @since 4.0
* @see AnnotationMetadata
* @see MethodMetadata
*/
public interface AnnotatedTypeMetadata {
/**
* Determine whether the underlying type has an annotation or
* meta-annotation of the given type defined.
* <p>If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying method, as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
* a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying method, as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
* a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType,
boolean classValuesAsString);
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
@ -25,11 +24,13 @@ import java.util.Set;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Phillip Webb
* @since 2.5 * @since 2.5
* @see StandardAnnotationMetadata * @see StandardAnnotationMetadata
* @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata() * @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
* @see AnnotatedTypeMetadata
*/ */
public interface AnnotationMetadata extends ClassMetadata { public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
/** /**
* Return the names of all annotation types defined on the underlying class. * Return the names of all annotation types defined on the underlying class.
@ -61,42 +62,6 @@ public interface AnnotationMetadata extends ClassMetadata {
*/ */
boolean hasMetaAnnotation(String metaAnnotationType); boolean hasMetaAnnotation(String metaAnnotationType);
/**
* Determine whether the underlying class has an annotation or
* meta-annotation of the given type defined.
* <p>This is equivalent to a "hasAnnotation || hasMetaAnnotation"
* check. If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/** /**
* Determine whether the underlying class has any methods that are * Determine whether the underlying class has any methods that are
* annotated (or meta-annotated) with the given annotation type. * annotated (or meta-annotated) with the given annotation type.

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.util.Map;
/** /**
* Interface that defines abstract access to the annotations of a specific * Interface that defines abstract access to the annotations of a specific
* class, in a form that does not require that class to be loaded yet. * class, in a form that does not require that class to be loaded yet.
@ -25,11 +23,13 @@ import java.util.Map;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Pollack * @author Mark Pollack
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
* @see StandardMethodMetadata * @see StandardMethodMetadata
* @see AnnotationMetadata#getAnnotatedMethods * @see AnnotationMetadata#getAnnotatedMethods
* @see AnnotatedTypeMetadata
*/ */
public interface MethodMetadata { public interface MethodMetadata extends AnnotatedTypeMetadata {
/** /**
* Return the name of the method. * Return the name of the method.
@ -57,23 +57,4 @@ public interface MethodMetadata {
*/ */
boolean isOverridable(); boolean isOverridable();
/**
* Determine whether the underlying method has an annotation or
* meta-annotation of the given type defined.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying method, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
} }

View File

@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.MultiValueMap;
/** /**
* {@link AnnotationMetadata} implementation that uses standard reflection * {@link AnnotationMetadata} implementation that uses standard reflection
@ -32,6 +32,7 @@ import org.springframework.core.annotation.AnnotationUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 2.5 * @since 2.5
*/ */
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata { public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
@ -74,21 +75,7 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
public Set<String> getMetaAnnotationTypes(String annotationType) { public Set<String> getMetaAnnotationTypes(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
Set<String> types = new LinkedHashSet<String>();
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
types.add(metaAnn.annotationType().getName());
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
types.add(metaMetaAnn.annotationType().getName());
}
}
return types;
}
}
return null;
} }
public boolean hasAnnotation(String annotationType) { public boolean hasAnnotation(String annotationType) {
@ -102,76 +89,39 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
} }
public boolean hasMetaAnnotation(String annotationType) { public boolean hasMetaAnnotation(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
Annotation[] metaAnns = ann.annotationType().getAnnotations();
for (Annotation metaAnn : metaAnns) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaMetaAnn : metaAnn.annotationType().getAnnotations()) {
if (metaMetaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
}
return false;
} }
public boolean isAnnotated(String annotationType) { public boolean isAnnotated(String annotationType) {
Annotation[] anns = getIntrospectedClass().getAnnotations(); return AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
return false;
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.getAnnotationAttributes(annotationType, false); return this.getAnnotationAttributes(annotationType, false);
} }
public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) { public Map<String, Object> getAnnotationAttributes(String annotationType,
Annotation[] anns = getIntrospectedClass().getAnnotations(); boolean classValuesAsString) {
for (Annotation ann : anns) { return AnnotatedElementUtils.getAnnotationAttributes(getIntrospectedClass(),
if (ann.annotationType().getName().equals(annotationType)) { annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
return AnnotationUtils.getAnnotationAttributes(
ann, classValuesAsString, this.nestedAnnotationsAsMap);
} }
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
} }
for (Annotation ann : anns) {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) { public MultiValueMap<String, Object> getAllAnnotationAttributes(
if (metaAnn.annotationType().getName().equals(annotationType)) { String annotationType, boolean classValuesAsString) {
return AnnotationUtils.getAnnotationAttributes( return AnnotatedElementUtils.getAllAnnotationAttributes(getIntrospectedClass(),
metaAnn, classValuesAsString, this.nestedAnnotationsAsMap); annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
}
}
}
return null;
} }
public boolean hasAnnotatedMethods(String annotationType) { public boolean hasAnnotatedMethods(String annotationType) {
Method[] methods = getIntrospectedClass().getDeclaredMethods(); Method[] methods = getIntrospectedClass().getDeclaredMethods();
for (Method method : methods) { for (Method method : methods) {
for (Annotation ann : method.getAnnotations()) { if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
if (ann.annotationType().getName().equals(annotationType)) {
return true; return true;
} }
else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
}
} }
return false; return false;
} }
@ -180,19 +130,9 @@ public class StandardAnnotationMetadata extends StandardClassMetadata implements
Method[] methods = getIntrospectedClass().getDeclaredMethods(); Method[] methods = getIntrospectedClass().getDeclaredMethods();
Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>(); Set<MethodMetadata> annotatedMethods = new LinkedHashSet<MethodMetadata>();
for (Method method : methods) { for (Method method : methods) {
for (Annotation ann : method.getAnnotations()) { if (AnnotatedElementUtils.isAnnotated(method, annotationType)) {
if (ann.annotationType().getName().equals(annotationType)) { annotatedMethods.add(new StandardMethodMetadata(method,
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap)); this.nestedAnnotationsAsMap));
break;
}
else {
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
break;
}
}
}
} }
} }
return annotatedMethods; return annotatedMethods;

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,13 +16,12 @@
package org.springframework.core.type; package org.springframework.core.type;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Map; import java.util.Map;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
/** /**
* {@link MethodMetadata} implementation that uses standard reflection * {@link MethodMetadata} implementation that uses standard reflection
@ -31,6 +30,7 @@ import org.springframework.util.Assert;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Pollack * @author Mark Pollack
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
public class StandardMethodMetadata implements MethodMetadata { public class StandardMethodMetadata implements MethodMetadata {
@ -89,35 +89,26 @@ public class StandardMethodMetadata implements MethodMetadata {
} }
public boolean isAnnotated(String annotationType) { public boolean isAnnotated(String annotationType) {
Annotation[] anns = this.introspectedMethod.getAnnotations(); return AnnotatedElementUtils.isAnnotated(this.introspectedMethod, annotationType);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return true;
}
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) {
return true;
}
}
}
return false;
} }
public Map<String, Object> getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
Annotation[] anns = this.introspectedMethod.getAnnotations(); return getAnnotationAttributes(annotationType, false);
for (Annotation ann : anns) {
if (ann.annotationType().getName().equals(annotationType)) {
return AnnotationUtils.getAnnotationAttributes(
ann, true, nestedAnnotationsAsMap);
} }
for (Annotation metaAnn : ann.annotationType().getAnnotations()) {
if (metaAnn.annotationType().getName().equals(annotationType)) { public Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
return AnnotationUtils.getAnnotationAttributes( return AnnotatedElementUtils.getAnnotationAttributes(this.introspectedMethod,
metaAnn, true, this.nestedAnnotationsAsMap); annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
} }
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
} }
}
return null; public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
return AnnotatedElementUtils.getAllAnnotationAttributes(this.introspectedMethod,
annotationType, classValuesAsString, this.nestedAnnotationsAsMap);
} }
} }

View File

@ -34,13 +34,14 @@ import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
/** /**
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Phillip Webb
* @since 3.1.1 * @since 3.1.1
*/ */
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
@ -51,12 +52,14 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
protected final ClassLoader classLoader; protected final ClassLoader classLoader;
public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) { public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) {
super(SpringAsmInfo.ASM_VERSION); super(SpringAsmInfo.ASM_VERSION);
this.classLoader = classLoader; this.classLoader = classLoader;
this.attributes = attributes; this.attributes = attributes;
} }
public void visit(String attributeName, Object attributeValue) { public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue); this.attributes.put(attributeName, attributeValue);
} }
@ -108,12 +111,14 @@ final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationV
private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>(); private final List<AnnotationAttributes> allNestedAttributes = new ArrayList<AnnotationAttributes>();
public RecursiveAnnotationArrayVisitor( public RecursiveAnnotationArrayVisitor(
String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) { String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes); super(classLoader, attributes);
this.attributeName = attributeName; this.attributeName = attributeName;
} }
@Override @Override
public void visit(String attributeName, Object attributeValue) { public void visit(String attributeName, Object attributeValue) {
Object newValue = attributeValue; Object newValue = attributeValue;
@ -155,12 +160,14 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
private final String annotationType; private final String annotationType;
public RecursiveAnnotationAttributesVisitor( public RecursiveAnnotationAttributesVisitor(
String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) { String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) {
super(classLoader, attributes); super(classLoader, attributes);
this.annotationType = annotationType; this.annotationType = annotationType;
} }
public final void visitEnd() { public final void visitEnd() {
try { try {
Class<?> annotationClass = this.classLoader.loadClass(this.annotationType); Class<?> annotationClass = this.classLoader.loadClass(this.annotationType);
@ -214,18 +221,20 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor { final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
private final String annotationType; private final String annotationType;
private final Map<String, AnnotationAttributes> attributesMap; private final MultiValueMap<String, AnnotationAttributes> attributesMap;
private final Map<String, Set<String>> metaAnnotationMap; private final Map<String, Set<String>> metaAnnotationMap;
public AnnotationAttributesReadingVisitor( public AnnotationAttributesReadingVisitor(
String annotationType, Map<String, AnnotationAttributes> attributesMap, String annotationType, MultiValueMap<String, AnnotationAttributes> attributesMap,
Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) { Map<String, Set<String>> metaAnnotationMap, ClassLoader classLoader) {
super(annotationType, new AnnotationAttributes(), classLoader); super(annotationType, new AnnotationAttributes(), classLoader);
@ -234,29 +243,33 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib
this.metaAnnotationMap = metaAnnotationMap; this.metaAnnotationMap = metaAnnotationMap;
} }
@Override @Override
public void doVisitEnd(Class<?> annotationClass) { public void doVisitEnd(Class<?> annotationClass) {
super.doVisitEnd(annotationClass); super.doVisitEnd(annotationClass);
this.attributesMap.put(this.annotationType, this.attributes); List<AnnotationAttributes> attributes = this.attributesMap.get(this.annotationType);
registerMetaAnnotations(annotationClass); if (attributes == null) {
this.attributesMap.add(this.annotationType, this.attributes);
}
else {
attributes.add(0, this.attributes);
} }
private void registerMetaAnnotations(Class<?> annotationClass) {
// Register annotations that the annotation type is annotated with.
Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(); Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>();
for (Annotation metaAnnotation : annotationClass.getAnnotations()) { for (Annotation metaAnnotation : annotationClass.getAnnotations()) {
metaAnnotationTypeNames.add(metaAnnotation.annotationType().getName()); recusivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation);
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) { if (this.metaAnnotationMap != null) {
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
} }
} }
private void recusivelyCollectMetaAnnotations(Set<String> visited, Annotation annotation) {
if (visited.add(annotation.annotationType().getName())) {
this.attributesMap.add(annotation.annotationType().getName(),
AnnotationUtils.getAnnotationAttributes(annotation, true, true));
for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) {
recusivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
}
}
}
} }

View File

@ -41,6 +41,7 @@ import org.springframework.util.MultiValueMap;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Mark Fisher * @author Mark Fisher
* @author Costin Leau * @author Costin Leau
* @author Phillip Webb
* @since 2.5 * @since 2.5
*/ */
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata { public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
@ -51,7 +52,7 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4); protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<String, Set<String>>(4);
protected final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(4); protected final MultiValueMap<String, AnnotationAttributes> attributeMap = new LinkedMultiValueMap<String, AnnotationAttributes>(4);
protected final MultiValueMap<String, MethodMetadata> methodMetadataMap = new LinkedMultiValueMap<String, MethodMetadata>(); protected final MultiValueMap<String, MethodMetadata> methodMetadataMap = new LinkedMultiValueMap<String, MethodMetadata>();
@ -105,60 +106,30 @@ public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisito
} }
public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) { public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) {
AnnotationAttributes raw = this.attributeMap.get(annotationType); List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
return convertClassValues(raw, classValuesAsString); AnnotationAttributes raw = (attributes == null ? null : attributes.get(0));
return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw,
classValuesAsString);
} }
private AnnotationAttributes convertClassValues(AnnotationAttributes original, boolean classValuesAsString) { public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
if (original == null) { return getAllAnnotationAttributes(annotationType, false);
}
public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
if (attributes == null) {
return null; return null;
} }
AnnotationAttributes result = new AnnotationAttributes(original.size()); for (AnnotationAttributes raw : attributes) {
for (Map.Entry<String, Object> entry : original.entrySet()) { for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
try { this.classLoader, raw, classValuesAsString).entrySet()) {
Object value = entry.getValue(); allAttributes.add(entry.getKey(), entry.getValue());
if (value instanceof AnnotationAttributes) {
value = convertClassValues((AnnotationAttributes) value, classValuesAsString);
}
else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[])value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(values[i], classValuesAsString);
} }
} }
else if (value instanceof Type) { return allAttributes;
value = (classValuesAsString ? ((Type) value).getClassName() :
this.classLoader.loadClass(((Type) value).getClassName()));
}
else if (value instanceof Type[]) {
Type[] array = (Type[]) value;
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]);
for (int i = 0; i < array.length; i++) {
convArray[i] = (classValuesAsString ? array[i].getClassName() :
this.classLoader.loadClass(array[i].getClassName()));
}
value = convArray;
}
else 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;
}
}
result.put(entry.getKey(), value);
}
catch (Exception ex) {
// Class not found - can't resolve class reference in annotation attribute.
}
}
return result;
} }
public boolean hasAnnotatedMethods(String annotationType) { public boolean hasAnnotatedMethods(String annotationType) {

View File

@ -0,0 +1,92 @@
/*
* 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 org.springframework.core.type.classreading;
import java.util.Map;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
/**
* Internal utility class used when reading annotations.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Costin Leau
* @author Phillip Webb
* @since 4.0
*/
abstract class AnnotationReadingVisitorUtils {
public static AnnotationAttributes convertClassValues(ClassLoader classLoader,
AnnotationAttributes original, boolean classValuesAsString) {
if (original == null) {
return null;
}
AnnotationAttributes result = new AnnotationAttributes(original.size());
for (Map.Entry<String, Object> entry : original.entrySet()) {
try {
Object value = entry.getValue();
if (value instanceof AnnotationAttributes) {
value = convertClassValues(classLoader, (AnnotationAttributes) value,
classValuesAsString);
}
else if (value instanceof AnnotationAttributes[]) {
AnnotationAttributes[] values = (AnnotationAttributes[])value;
for (int i = 0; i < values.length; i++) {
values[i] = convertClassValues(classLoader, values[i],
classValuesAsString);
}
}
else if (value instanceof Type) {
value = (classValuesAsString ? ((Type) value).getClassName() :
classLoader.loadClass(((Type) value).getClassName()));
}
else if (value instanceof Type[]) {
Type[] array = (Type[]) value;
Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]);
for (int i = 0; i < array.length; i++) {
convArray[i] = (classValuesAsString ? array[i].getClassName() :
classLoader.loadClass(array[i].getClassName()));
}
value = convArray;
}
else 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;
}
}
result.put(entry.getKey(), value);
}
catch (Exception ex) {
// Class not found - can't resolve class reference in annotation attribute.
}
}
return result;
}
}

View File

@ -16,7 +16,7 @@
package org.springframework.core.type.classreading; package org.springframework.core.type.classreading;
import java.util.LinkedHashMap; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.AnnotationVisitor;
@ -26,6 +26,7 @@ import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type; import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.MethodMetadata;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
/** /**
@ -37,6 +38,7 @@ import org.springframework.util.MultiValueMap;
* @author Mark Pollack * @author Mark Pollack
* @author Costin Leau * @author Costin Leau
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
* @since 3.0 * @since 3.0
*/ */
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata { public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
@ -51,7 +53,7 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
protected final MultiValueMap<String, MethodMetadata> methodMetadataMap; protected final MultiValueMap<String, MethodMetadata> methodMetadataMap;
protected final Map<String, AnnotationAttributes> attributeMap = new LinkedHashMap<String, AnnotationAttributes>(2); protected final MultiValueMap<String, AnnotationAttributes> attributeMap = new LinkedMultiValueMap<String, AnnotationAttributes>(2);
public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader, public MethodMetadataReadingVisitor(String name, int access, String declaringClassName, ClassLoader classLoader,
@ -93,8 +95,34 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
return this.attributeMap.containsKey(annotationType); return this.attributeMap.containsKey(annotationType);
} }
public AnnotationAttributes getAnnotationAttributes(String annotationType) { public Map<String, Object> getAnnotationAttributes(String annotationType) {
return this.attributeMap.get(annotationType); return getAnnotationAttributes(annotationType, false);
}
public Map<String, Object> getAnnotationAttributes(String annotationType,
boolean classValuesAsString) {
List<AnnotationAttributes> attributes = this.attributeMap.get(annotationType);
return (attributes == null ? null : AnnotationReadingVisitorUtils.convertClassValues(
this.classLoader, attributes.get(0), classValuesAsString));
}
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType) {
return getAllAnnotationAttributes(annotationType, false);
}
public MultiValueMap<String, Object> getAllAnnotationAttributes(
String annotationType, boolean classValuesAsString) {
if(!this.attributeMap.containsKey(annotationType)) {
return null;
}
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<String, Object>();
for (AnnotationAttributes annotationAttributes : this.attributeMap.get(annotationType)) {
for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues(
this.classLoader, annotationAttributes, classValuesAsString).entrySet()) {
allAttributes.add(entry.getKey(), entry.getValue());
}
}
return allAttributes;
} }
public String getDeclaringClassName() { public String getDeclaringClassName() {

View File

@ -18,7 +18,9 @@ package org.springframework.core.type;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -29,10 +31,12 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.junit.Test; import org.junit.Test;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
@ -45,6 +49,7 @@ import org.springframework.stereotype.Component;
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Chris Beams * @author Chris Beams
* @author Phillip Webb
*/ */
public class AnnotationMetadataTests { public class AnnotationMetadataTests {
@ -93,7 +98,7 @@ public class AnnotationMetadataTests {
assertThat(metadata.hasAnnotation(Component.class.getName()), is(true)); assertThat(metadata.hasAnnotation(Component.class.getName()), is(true));
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true)); assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true));
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true)); assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().size(), is(3)); assertThat(metadata.getAnnotationTypes().size(), is(5));
assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true));
@ -104,6 +109,15 @@ public class AnnotationMetadataTests {
AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName()); AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName());
assertThat(scopeAttrs.size(), is(1)); assertThat(scopeAttrs.size(), is(1));
assertThat(scopeAttrs.getString("value"), is("myScope")); assertThat(scopeAttrs.getString("value"), is("myScope"));
Set<MethodMetadata> methods = metadata.getAnnotatedMethods(DirectAnnotation.class.getName());
MethodMetadata method = methods.iterator().next();
assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"));
List<Object> allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
assertTrue(metadata.isAnnotated(IsAnnotatedAnnotation.class.getName()));
{ // perform tests with classValuesAsString = false (the default) { // perform tests with classValuesAsString = false (the default)
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName()); AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName());
assertThat(specialAttrs.size(), is(6)); assertThat(specialAttrs.size(), is(6));
@ -137,6 +151,10 @@ public class AnnotationMetadataTests {
assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT)); assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT));
assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray")); assertArrayEquals(new Class[]{Void.class}, (Class[])optionalArray[0].get("classArray"));
assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray")); assertArrayEquals(new Class[]{Void.class}, optionalArray[0].getClassArray("classArray"));
assertEquals("direct", metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"));
allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
} }
{ // perform tests with classValuesAsString = true { // perform tests with classValuesAsString = true
AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true); AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true);
@ -161,6 +179,10 @@ public class AnnotationMetadataTests {
AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray"); AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray");
assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("classArray")); assertArrayEquals(new String[]{Void.class.getName()}, (String[])optionalArray[0].get("classArray"));
assertArrayEquals(new String[]{Void.class.getName()}, optionalArray[0].getStringArray("classArray")); assertArrayEquals(new String[]{Void.class.getName()}, optionalArray[0].getStringArray("classArray"));
assertEquals(metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"), "direct");
allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value");
assertThat(new HashSet<Object>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta")))));
} }
} }
@ -201,6 +223,29 @@ public class AnnotationMetadataTests {
NestedAnno[] optionalArray() default {@NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class)}; NestedAnno[] optionalArray() default {@NestedAnno(value="optional", anEnum=SomeEnum.DEFAULT, classArray=Void.class)};
} }
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DirectAnnotation {
String value();
}
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface IsAnnotatedAnnotation {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@DirectAnnotation("meta")
@IsAnnotatedAnnotation
public @interface MetaAnnotation {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@MetaAnnotation
public @interface MetaMetaAnnotation {
}
@Component("myName") @Component("myName")
@Scope("myScope") @Scope("myScope")
@ -211,6 +256,8 @@ public class AnnotationMetadataTests {
@NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class}) @NestedAnno(value = "na1", anEnum = SomeEnum.LABEL2, classArray = {Number.class})
}) })
@SuppressWarnings({"serial", "unused"}) @SuppressWarnings({"serial", "unused"})
@DirectAnnotation("direct")
@MetaMetaAnnotation
private static class AnnotatedComponent implements Serializable { private static class AnnotatedComponent implements Serializable {
@TestAutowired @TestAutowired
@ -219,6 +266,11 @@ public class AnnotationMetadataTests {
public void doSleep() { public void doSleep() {
} }
@DirectAnnotation("direct")
@MetaMetaAnnotation
public void meta() {
}
} }
} }