Skip to content

Commit 0f5a27c

Browse files
committed
Merge changes from sbrannen/SPR-11475
* SPR-11475: Favor 'local' annotations over inherited ones
2 parents 0616cbc + 1d30bf8 commit 0f5a27c

File tree

4 files changed

+217
-79
lines changed

4 files changed

+217
-79
lines changed

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

Lines changed: 111 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.springframework.util.LinkedMultiValueMap;
2727
import org.springframework.util.MultiValueMap;
2828

29+
import static org.springframework.core.annotation.AnnotationUtils.*;
30+
2931
/**
3032
* Utility class used to collect all annotation values including those declared on
3133
* meta-annotations.
@@ -39,14 +41,16 @@ public class AnnotatedElementUtils {
3941

4042
public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
4143
final Set<String> types = new LinkedHashSet<String>();
42-
process(element, annotationType, new Processor<Object>() {
44+
process(element, annotationType, true, new Processor<Object>() {
45+
4346
@Override
44-
public Object process(Annotation annotation, int depth) {
45-
if (depth > 0) {
47+
public Object process(Annotation annotation, int metaDepth) {
48+
if (metaDepth > 0) {
4649
types.add(annotation.annotationType().getName());
4750
}
4851
return null;
4952
}
53+
5054
@Override
5155
public void postProcess(Annotation annotation, Object result) {
5256
}
@@ -55,26 +59,30 @@ public void postProcess(Annotation annotation, Object result) {
5559
}
5660

5761
public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
58-
return Boolean.TRUE.equals(process(element, annotationType, new Processor<Boolean>() {
62+
return Boolean.TRUE.equals(process(element, annotationType, true, new Processor<Boolean>() {
63+
5964
@Override
60-
public Boolean process(Annotation annotation, int depth) {
61-
if (depth > 0) {
62-
return true;
65+
public Boolean process(Annotation annotation, int metaDepth) {
66+
if (metaDepth > 0) {
67+
return Boolean.TRUE;
6368
}
6469
return null;
6570
}
71+
6672
@Override
6773
public void postProcess(Annotation annotation, Boolean result) {
6874
}
6975
}));
7076
}
7177

7278
public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
73-
return Boolean.TRUE.equals(process(element, annotationType, new Processor<Boolean>() {
79+
return Boolean.TRUE.equals(process(element, annotationType, true, new Processor<Boolean>() {
80+
7481
@Override
75-
public Boolean process(Annotation annotation, int depth) {
76-
return true;
82+
public Boolean process(Annotation annotation, int metaDepth) {
83+
return Boolean.TRUE;
7784
}
85+
7886
@Override
7987
public void postProcess(Annotation annotation, Boolean result) {
8088
}
@@ -85,20 +93,21 @@ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement elem
8593
return getAnnotationAttributes(element, annotationType, false, false);
8694
}
8795

88-
public static AnnotationAttributes getAnnotationAttributes(
89-
AnnotatedElement element, String annotationType,
96+
public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType,
9097
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
9198

92-
return process(element, annotationType, new Processor<AnnotationAttributes>() {
99+
return process(element, annotationType, true, new Processor<AnnotationAttributes>() {
100+
93101
@Override
94-
public AnnotationAttributes process(Annotation annotation, int depth) {
102+
public AnnotationAttributes process(Annotation annotation, int metaDepth) {
95103
return AnnotationUtils.getAnnotationAttributes(annotation, classValuesAsString, nestedAnnotationsAsMap);
96104
}
105+
97106
@Override
98107
public void postProcess(Annotation annotation, AnnotationAttributes result) {
99108
for (String key : result.keySet()) {
100-
if (!"value".equals(key)) {
101-
Object value = AnnotationUtils.getValue(annotation, key);
109+
if (!VALUE.equals(key)) {
110+
Object value = getValue(annotation, key);
102111
if (value != null) {
103112
result.put(key, value);
104113
}
@@ -108,28 +117,33 @@ public void postProcess(Annotation annotation, AnnotationAttributes result) {
108117
});
109118
}
110119

111-
public static MultiValueMap<String, Object> getAllAnnotationAttributes(
112-
AnnotatedElement element, final String annotationType,
113-
final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
120+
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
121+
final String annotationType) {
122+
return getAllAnnotationAttributes(element, annotationType, false, false);
123+
}
124+
125+
public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
126+
final String annotationType, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
114127

115128
final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
116-
process(element, annotationType, new Processor<Void>() {
129+
process(element, annotationType, false, new Processor<Void>() {
130+
117131
@Override
118-
public Void process(Annotation annotation, int depth) {
132+
public Void process(Annotation annotation, int metaDepth) {
119133
if (annotation.annotationType().getName().equals(annotationType)) {
120-
for (Map.Entry<String, Object> entry :
121-
AnnotationUtils.getAnnotationAttributes(
122-
annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
134+
for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(annotation,
135+
classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
123136
attributes.add(entry.getKey(), entry.getValue());
124137
}
125138
}
126139
return null;
127140
}
141+
128142
@Override
129143
public void postProcess(Annotation annotation, Void result) {
130144
for (String key : attributes.keySet()) {
131-
if (!"value".equals(key)) {
132-
Object value = AnnotationUtils.getValue(annotation, key);
145+
if (!VALUE.equals(key)) {
146+
Object value = getValue(annotation, key);
133147
if (value != null) {
134148
attributes.add(key, value);
135149
}
@@ -141,45 +155,91 @@ public void postProcess(Annotation annotation, Void result) {
141155
}
142156

143157
/**
144-
* Process all annotations of the specified annotation type and recursively all
145-
* meta-annotations on the specified type.
158+
* Process all annotations of the specified {@code annotationType} and
159+
* recursively all meta-annotations on the specified {@code element}.
160+
*
161+
* <p>If the {@code traverseClassHierarchy} flag is {@code true} and the sought
162+
* annotation is neither <em>directly present</em> on the given element nor
163+
* present on the given element as a meta-annotation, then the algorithm will
164+
* recursively search through the class hierarchy of the given element.
165+
*
146166
* @param element the annotated element
147-
* @param annotationType the annotation type to find. Only items of the specified
148-
* type or meta-annotations of the specified type will be processed.
149-
* @param processor the processor
167+
* @param annotationType the annotation type to find
168+
* @param traverseClassHierarchy whether or not to traverse up the class
169+
* hierarchy recursively
170+
* @param processor the processor to delegate to
150171
* @return the result of the processor
151172
*/
152-
private static <T> T process(AnnotatedElement element, String annotationType, Processor<T> processor) {
153-
return doProcess(element, annotationType, processor, new HashSet<AnnotatedElement>(), 0);
173+
private static <T> T process(AnnotatedElement element, String annotationType, boolean traverseClassHierarchy,
174+
Processor<T> processor) {
175+
return doProcess(element, annotationType, traverseClassHierarchy, processor, new HashSet<AnnotatedElement>(), 0);
154176
}
155177

156-
private static <T> T doProcess(AnnotatedElement element, String annotationType,
157-
Processor<T> processor, Set<AnnotatedElement> visited, int depth) {
178+
/**
179+
* Perform the search algorithm for the {@link #process} method, avoiding
180+
* endless recursion by tracking which annotated elements have already been
181+
* <em>visited</em>.
182+
*
183+
* <p>The {@code metaDepth} parameter represents the depth of the annotation
184+
* relative to the initial element. For example, an annotation that is
185+
* <em>present</em> on the element will have a depth of 0; a meta-annotation
186+
* will have a depth of 1; and a meta-meta-annotation will have a depth of 2.
187+
*
188+
* @param element the annotated element
189+
* @param annotationType the annotation type to find
190+
* @param traverseClassHierarchy whether or not to traverse up the class
191+
* hierarchy recursively
192+
* @param processor the processor to delegate to
193+
* @param visited the set of annotated elements that have already been visited
194+
* @param metaDepth the depth of the annotation relative to the initial element
195+
* @return the result of the processor
196+
*/
197+
private static <T> T doProcess(AnnotatedElement element, String annotationType, boolean traverseClassHierarchy,
198+
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
158199

159200
if (visited.add(element)) {
160-
for (Annotation annotation : element.getAnnotations()) {
161-
if (annotation.annotationType().getName().equals(annotationType) || depth > 0) {
162-
T result = processor.process(annotation, depth);
201+
202+
Annotation[] annotations = traverseClassHierarchy ? element.getDeclaredAnnotations()
203+
: element.getAnnotations();
204+
205+
for (Annotation annotation : annotations) {
206+
if (annotation.annotationType().getName().equals(annotationType) || metaDepth > 0) {
207+
T result = processor.process(annotation, metaDepth);
163208
if (result != null) {
164209
return result;
165210
}
166-
result = doProcess(annotation.annotationType(), annotationType, processor, visited, depth + 1);
211+
result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy, processor,
212+
visited, metaDepth + 1);
167213
if (result != null) {
168214
processor.postProcess(annotation, result);
169215
return result;
170216
}
171217
}
172218
}
173-
for (Annotation annotation : element.getAnnotations()) {
174-
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
175-
T result = doProcess(annotation.annotationType(), annotationType, processor, visited, depth);
219+
220+
for (Annotation annotation : annotations) {
221+
if (!isInJavaLangAnnotationPackage(annotation)) {
222+
T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
223+
processor, visited, metaDepth);
176224
if (result != null) {
177225
processor.postProcess(annotation, result);
178226
return result;
179227
}
180228
}
181229
}
230+
231+
if (traverseClassHierarchy && element instanceof Class) {
232+
Class<?> superclass = ((Class<?>) element).getSuperclass();
233+
if (superclass != null && !superclass.equals(Object.class)) {
234+
T result = doProcess(superclass, annotationType, traverseClassHierarchy, processor, visited,
235+
metaDepth);
236+
if (result != null) {
237+
return result;
238+
}
239+
}
240+
}
182241
}
242+
183243
return null;
184244
}
185245

@@ -192,13 +252,18 @@ private static interface Processor<T> {
192252

193253
/**
194254
* Called to process the annotation.
255+
*
256+
* <p>The {@code metaDepth} parameter represents the depth of the
257+
* annotation relative to the initial element. For example, an annotation
258+
* that is <em>present</em> on the element will have a depth of 0; a
259+
* meta-annotation will have a depth of 1; and a meta-meta-annotation
260+
* will have a depth of 2.
261+
*
195262
* @param annotation the annotation to process
196-
* @param depth the depth of the annotation relative to the initial match.
197-
* For example, a matched annotation will have a depth of 0, a meta-annotation
198-
* 1 and a meta-meta-annotation 2
263+
* @param metaDepth the depth of the annotation relative to the initial element
199264
* @return the result of the processing or {@code null} to continue
200265
*/
201-
T process(Annotation annotation, int depth);
266+
T process(Annotation annotation, int metaDepth);
202267

203268
void postProcess(Annotation annotation, T result);
204269
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A>
278278
Set<Annotation> visited) {
279279
Assert.notNull(clazz, "Class must not be null");
280280

281-
A annotation = clazz.getAnnotation(annotationType);
281+
A annotation = clazz.getDeclaredAnnotation(annotationType);
282282
if (annotation != null) {
283283
return annotation;
284284
}
@@ -288,7 +288,7 @@ private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A>
288288
return annotation;
289289
}
290290
}
291-
for (Annotation ann : clazz.getAnnotations()) {
291+
for (Annotation ann : clazz.getDeclaredAnnotations()) {
292292
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
293293
annotation = findAnnotation(ann.annotationType(), annotationType, visited);
294294
if (annotation != null) {

0 commit comments

Comments
 (0)