Skip to content

Commit e6bde2b

Browse files
committed
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.
1 parent c6666b2 commit e6bde2b

11 files changed

+539
-254
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.type;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
21+
import java.util.HashSet;
22+
import java.util.LinkedHashSet;
23+
import java.util.Map;
24+
import java.util.Set;
25+
26+
import org.springframework.core.annotation.AnnotationUtils;
27+
import org.springframework.util.LinkedMultiValueMap;
28+
import org.springframework.util.MultiValueMap;
29+
30+
/**
31+
* Internal utility class used to collect all annotation values including those declared
32+
* on meta-annotations.
33+
*
34+
* @author Phillip Webb
35+
* @since 4.0
36+
*/
37+
class AnnotatedElementUtils {
38+
39+
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element,
40+
String annotationType) {
41+
final Set<String> types = new LinkedHashSet<String>();
42+
process(element, annotationType, new Processor<Object>() {
43+
public Object process(Annotation annotation, int depth) {
44+
if (depth > 0) {
45+
types.add(annotation.annotationType().getName());
46+
}
47+
return null;
48+
}
49+
});
50+
return (types.size() == 0 ? null : types);
51+
}
52+
53+
public static boolean hasMetaAnnotationTypes(AnnotatedElement element,
54+
String annotationType) {
55+
return Boolean.TRUE.equals(
56+
process(element, annotationType, new Processor<Boolean>() {
57+
public Boolean process(Annotation annotation, int depth) {
58+
if (depth > 0) {
59+
return true;
60+
}
61+
return null;
62+
}
63+
}));
64+
}
65+
66+
public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
67+
return Boolean.TRUE.equals(
68+
process(element, annotationType, new Processor<Boolean>() {
69+
public Boolean process(Annotation annotation, int depth) {
70+
return true;
71+
}
72+
}));
73+
}
74+
75+
public static Map<String, Object> getAnnotationAttributes(AnnotatedElement element,
76+
String annotationType, final boolean classValuesAsString,
77+
final boolean nestedAnnotationsAsMap) {
78+
return process(element, annotationType, new Processor<Map<String, Object>>() {
79+
public Map<String, Object> process(Annotation annotation, int depth) {
80+
return AnnotationUtils.getAnnotationAttributes(annotation,
81+
classValuesAsString, nestedAnnotationsAsMap);
82+
}
83+
});
84+
}
85+
86+
public static MultiValueMap<String, Object> getAllAnnotationAttributes(
87+
AnnotatedElement element, final String annotationType,
88+
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
89+
final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
90+
process(element, annotationType, new Processor<Object>() {
91+
public Object process(Annotation annotation, int depth) {
92+
if (annotation.annotationType().getName().equals(annotationType)) {
93+
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
94+
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
95+
attributes.add(entry.getKey(), entry.getValue());
96+
}
97+
}
98+
return null;
99+
}
100+
});
101+
return (attributes.size() == 0 ? null : attributes);
102+
}
103+
104+
/**
105+
* Process all annotations of the specified annotation type and recursively all
106+
* meta-annotations on the specified type.
107+
* @param element the annotated element
108+
* @param annotationType the annotation type to find. Only items of the specified type
109+
* or meta-annotations of the specified type will be processed
110+
* @param processor the processor
111+
* @return the result of the processor
112+
*/
113+
private static <T> T process(AnnotatedElement element, String annotationType,
114+
Processor<T> processor) {
115+
return recursivelyProcess(element, annotationType, processor,
116+
new HashSet<AnnotatedElement>(), 0);
117+
}
118+
119+
private static <T> T recursivelyProcess(AnnotatedElement element,
120+
String annotationType, Processor<T> processor, Set<AnnotatedElement> visited,
121+
int depth) {
122+
T result = null;
123+
if (visited.add(element)) {
124+
for (Annotation annotation : element.getAnnotations()) {
125+
if (annotation.annotationType().getName().equals(annotationType) || depth > 0) {
126+
result = result != null ? result :
127+
processor.process(annotation, depth);
128+
result = result != null ? result :
129+
recursivelyProcess(annotation.annotationType(), annotationType,
130+
processor, visited, depth + 1);
131+
}
132+
}
133+
for (Annotation annotation : element.getAnnotations()) {
134+
result = result != null ? result :
135+
recursivelyProcess(annotation.annotationType(), annotationType,
136+
processor, visited, depth);
137+
}
138+
139+
}
140+
return result;
141+
}
142+
143+
/**
144+
* Callback interface used to process an annotation.
145+
* @param <T> the result type
146+
*/
147+
private static interface Processor<T> {
148+
149+
/**
150+
* Called to process the annotation.
151+
* @param annotation the annotation to process
152+
* @param depth the depth of the annotation relative to the initial match. For
153+
* example a matched annotation will have a depth of 0, a meta-annotation 1
154+
* and a meta-meta-annotation 2
155+
* @return the result of the processing or {@code null} to continue
156+
*/
157+
T process(Annotation annotation, int depth);
158+
}
159+
160+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.type;
18+
19+
import java.util.Map;
20+
21+
import org.springframework.util.MultiValueMap;
22+
23+
/**
24+
* Defines access to the annotations of a specific type ({@link AnnotationMetadata class}
25+
* or {@link MethodMetadata method}), in a form that does not necessarily require the
26+
* class-loading.
27+
*
28+
* @author Juergen Hoeller
29+
* @author Mark Fisher
30+
* @author Mark Pollack
31+
* @author Chris Beams
32+
* @author Phillip Webb
33+
* @since 4.0
34+
* @see AnnotationMetadata
35+
* @see MethodMetadata
36+
*/
37+
public interface AnnotatedTypeMetadata {
38+
39+
/**
40+
* Determine whether the underlying type has an annotation or
41+
* meta-annotation of the given type defined.
42+
* <p>If this method returns {@code true}, then
43+
* {@link #getAnnotationAttributes} will return a non-null Map.
44+
* @param annotationType the annotation type to look for
45+
* @return whether a matching annotation is defined
46+
*/
47+
boolean isAnnotated(String annotationType);
48+
49+
/**
50+
* Retrieve the attributes of the annotation of the given type,
51+
* if any (i.e. if defined on the underlying class, as direct
52+
* annotation or as meta-annotation).
53+
* @param annotationType the annotation type to look for
54+
* @return a Map of attributes, with the attribute name as key (e.g. "value")
55+
* and the defined attribute value as Map value. This return value will be
56+
* {@code null} if no matching annotation is defined.
57+
*/
58+
Map<String, Object> getAnnotationAttributes(String annotationType);
59+
60+
/**
61+
* Retrieve the attributes of the annotation of the given type,
62+
* if any (i.e. if defined on the underlying class, as direct
63+
* annotation or as meta-annotation).
64+
* @param annotationType the annotation type to look for
65+
* @param classValuesAsString whether to convert class references to String
66+
* class names for exposure as values in the returned Map, instead of Class
67+
* references which might potentially have to be loaded first
68+
* @return a Map of attributes, with the attribute name as key (e.g. "value")
69+
* and the defined attribute value as Map value. This return value will be
70+
* {@code null} if no matching annotation is defined.
71+
*/
72+
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
73+
74+
/**
75+
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
76+
* defined on the underlying method, as direct annotation or as meta-annotation).
77+
* @param annotationType the annotation type to look for
78+
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
79+
* a list of the defined attribute values as Map value. This return value will
80+
* be {@code null} if no matching annotation is defined.
81+
*/
82+
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
83+
84+
/**
85+
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
86+
* defined on the underlying method, as direct annotation or as meta-annotation).
87+
* @param annotationType the annotation type to look for
88+
* @param classValuesAsString whether to convert class references to String
89+
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value") and
90+
* a list of the defined attribute values as Map value. This return value will
91+
* be {@code null} if no matching annotation is defined.
92+
* @see #getAllAnnotationAttributes(String)
93+
*/
94+
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType,
95+
boolean classValuesAsString);
96+
97+
}

spring-core/src/main/java/org/springframework/core/type/AnnotationMetadata.java

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.core.type;
1818

19-
import java.util.Map;
2019
import java.util.Set;
2120

2221
/**
@@ -25,11 +24,13 @@
2524
*
2625
* @author Juergen Hoeller
2726
* @author Mark Fisher
27+
* @author Phillip Webb
2828
* @since 2.5
2929
* @see StandardAnnotationMetadata
3030
* @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata()
31+
* @see AnnotatedTypeMetadata
3132
*/
32-
public interface AnnotationMetadata extends ClassMetadata {
33+
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
3334

3435
/**
3536
* Return the names of all annotation types defined on the underlying class.
@@ -61,42 +62,6 @@ public interface AnnotationMetadata extends ClassMetadata {
6162
*/
6263
boolean hasMetaAnnotation(String metaAnnotationType);
6364

64-
/**
65-
* Determine whether the underlying class has an annotation or
66-
* meta-annotation of the given type defined.
67-
* <p>This is equivalent to a "hasAnnotation || hasMetaAnnotation"
68-
* check. If this method returns {@code true}, then
69-
* {@link #getAnnotationAttributes} will return a non-null Map.
70-
* @param annotationType the annotation type to look for
71-
* @return whether a matching annotation is defined
72-
*/
73-
boolean isAnnotated(String annotationType);
74-
75-
/**
76-
* Retrieve the attributes of the annotation of the given type,
77-
* if any (i.e. if defined on the underlying class, as direct
78-
* annotation or as meta-annotation).
79-
* @param annotationType the annotation type to look for
80-
* @return a Map of attributes, with the attribute name as key (e.g. "value")
81-
* and the defined attribute value as Map value. This return value will be
82-
* {@code null} if no matching annotation is defined.
83-
*/
84-
Map<String, Object> getAnnotationAttributes(String annotationType);
85-
86-
/**
87-
* Retrieve the attributes of the annotation of the given type,
88-
* if any (i.e. if defined on the underlying class, as direct
89-
* annotation or as meta-annotation).
90-
* @param annotationType the annotation type to look for
91-
* @param classValuesAsString whether to convert class references to String
92-
* class names for exposure as values in the returned Map, instead of Class
93-
* references which might potentially have to be loaded first
94-
* @return a Map of attributes, with the attribute name as key (e.g. "value")
95-
* and the defined attribute value as Map value. This return value will be
96-
* {@code null} if no matching annotation is defined.
97-
*/
98-
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
99-
10065
/**
10166
* Determine whether the underlying class has any methods that are
10267
* annotated (or meta-annotated) with the given annotation type.

spring-core/src/main/java/org/springframework/core/type/MethodMetadata.java

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,20 +16,20 @@
1616

1717
package org.springframework.core.type;
1818

19-
import java.util.Map;
20-
2119
/**
2220
* Interface that defines abstract access to the annotations of a specific
2321
* class, in a form that does not require that class to be loaded yet.
2422
*
2523
* @author Juergen Hoeller
2624
* @author Mark Pollack
2725
* @author Chris Beams
26+
* @author Phillip Webb
2827
* @since 3.0
2928
* @see StandardMethodMetadata
3029
* @see AnnotationMetadata#getAnnotatedMethods
30+
* @see AnnotatedTypeMetadata
3131
*/
32-
public interface MethodMetadata {
32+
public interface MethodMetadata extends AnnotatedTypeMetadata {
3333

3434
/**
3535
* Return the name of the method.
@@ -57,23 +57,4 @@ public interface MethodMetadata {
5757
*/
5858
boolean isOverridable();
5959

60-
/**
61-
* Determine whether the underlying method has an annotation or
62-
* meta-annotation of the given type defined.
63-
* @param annotationType the annotation type to look for
64-
* @return whether a matching annotation is defined
65-
*/
66-
boolean isAnnotated(String annotationType);
67-
68-
/**
69-
* Retrieve the attributes of the annotation of the given type,
70-
* if any (i.e. if defined on the underlying method, as direct
71-
* annotation or as meta-annotation).
72-
* @param annotationType the annotation type to look for
73-
* @return a Map of attributes, with the attribute name as key (e.g. "value")
74-
* and the defined attribute value as Map value. This return value will be
75-
* {@code null} if no matching annotation is defined.
76-
*/
77-
Map<String, Object> getAnnotationAttributes(String annotationType);
78-
7960
}

0 commit comments

Comments
 (0)