AnnotationUtils.java 62.8 KB
Newer Older
A
Arjen Poutsma 已提交
1
/*
2
 * Copyright 2002-2015 the original author or authors.
A
Arjen Poutsma 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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.annotation;

import java.lang.annotation.Annotation;
20
import java.lang.reflect.AnnotatedElement;
21 22
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
A
Arjen Poutsma 已提交
23
import java.lang.reflect.Method;
24 25
import java.lang.reflect.Proxy;
import java.util.ArrayList;
26
import java.util.Collections;
27
import java.util.HashMap;
28 29
import java.util.HashSet;
import java.util.LinkedHashSet;
30
import java.util.List;
A
Arjen Poutsma 已提交
31
import java.util.Map;
32
import java.util.Set;
A
Arjen Poutsma 已提交
33

34 35
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
S
Sam Brannen 已提交
36

A
Arjen Poutsma 已提交
37 38
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
39
import org.springframework.util.ClassUtils;
40
import org.springframework.util.ConcurrentReferenceHashMap;
41
import org.springframework.util.ObjectUtils;
42
import org.springframework.util.ReflectionUtils;
43
import org.springframework.util.StringUtils;
A
Arjen Poutsma 已提交
44 45

/**
46 47 48 49 50 51
 * General utility methods for working with annotations, handling meta-annotations,
 * bridge methods (which the compiler generates for generic declarations) as well
 * as super methods (for optional <em>annotation inheritance</em>).
 *
 * <p>Note that most of the features of this class are not provided by the
 * JDK's introspection facilities themselves.
A
Arjen Poutsma 已提交
52
 *
S
Sam Brannen 已提交
53 54 55 56 57 58 59 60
 * <p>As a general rule for runtime-retained annotations (e.g. for transaction
 * control, authorization, or service exposure), always use the lookup methods
 * on this class (e.g., {@link #findAnnotation(Method, Class)},
 * {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)})
 * instead of the plain annotation lookup methods in the JDK. You can still
 * explicitly choose between a <em>get</em> lookup on the given class level only
 * ({@link #getAnnotation(Method, Class)}) and a <em>find</em> lookup in the entire
 * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}).
A
Arjen Poutsma 已提交
61
 *
62 63 64 65 66 67 68 69
 * <h3>Terminology</h3>
 * The terms <em>directly present</em> and <em>present</em> have the same
 * meanings as defined in the class-level Javadoc for {@link AnnotatedElement}.
 *
 * <p>An annotation is <em>meta-present</em> on an element if the annotation
 * is declared as a meta-annotation on some other annotation which is
 * <em>present</em> on the element.
 *
70
 * <h3>Meta-annotation Support</h3>
71 72 73 74 75
 * <p>Most {@code find*()} methods and some {@code get*()} methods in this
 * class provide support for finding annotations used as meta-annotations.
 * Consult the Javadoc for each method in this class for details. For support
 * for meta-annotations with <em>attribute overrides</em> in
 * <em>composed annotations</em>, use {@link AnnotatedElementUtils} instead.
76
 *
77 78 79 80 81 82
 * <h3>Search Scope</h3>
 * <p>The search algorithms used by methods in this class stop searching for
 * an annotation once the first annotation of the specified type has been
 * found. As a consequence, additional annotations of the specified type will
 * be silently ignored.
 *
A
Arjen Poutsma 已提交
83 84 85 86
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Mark Fisher
C
Chris Beams 已提交
87
 * @author Chris Beams
88
 * @author Phillip Webb
A
Arjen Poutsma 已提交
89
 * @since 2.0
S
Sam Brannen 已提交
90 91 92 93
 * @see AliasFor
 * @see AnnotationAttributes
 * @see AnnotatedElementUtils
 * @see BridgeMethodResolver
94 95 96
 * @see java.lang.reflect.AnnotatedElement#getAnnotations()
 * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class)
 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations()
A
Arjen Poutsma 已提交
97 98 99
 */
public abstract class AnnotationUtils {

100 101 102
	/**
	 * The attribute name for annotations with a single element.
	 */
103
	public static final String VALUE = "value";
A
Arjen Poutsma 已提交
104

105
	/**
S
Sam Brannen 已提交
106
	 * An object that can be stored in {@link AnnotationAttributes} as a
107 108 109 110
	 * placeholder for an attribute's declared default value.
	 */
	public static final Object DEFAULT_VALUE_PLACEHOLDER = "<SPRING DEFAULT VALUE PLACEHOLDER>";

111

112 113 114 115 116
	private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache =
			new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256);

	private static final Map<Class<?>, Boolean> annotatedInterfaceCache =
			new ConcurrentReferenceHashMap<Class<?>, Boolean>(256);
117

118 119 120
	private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache =
			new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256);

121 122 123
	private static final Map<Class<? extends Annotation>, Map<String, String>> attributeAliasCache =
			new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, String>>(256);

124 125
	private static transient Log logger;

J
Juergen Hoeller 已提交
126

127 128
	/**
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
129 130
	 * annotation: either the given annotation itself or a direct meta-annotation
	 * thereof.
131 132 133
	 * <p>Note that this method supports only a single level of meta-annotations.
	 * For support for arbitrary levels of meta-annotations, use one of the
	 * {@code find*()} methods instead.
134
	 * @param ann the Annotation to check
J
Juergen Hoeller 已提交
135
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
136
	 * @return the first matching annotation, or {@code null} if not found
137 138 139
	 * @since 4.0
	 */
	@SuppressWarnings("unchecked")
140
	public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType) {
141
		if (annotationType.isInstance(ann)) {
142
			return synthesizeAnnotation((A) ann);
143
		}
144
		Class<? extends Annotation> annotatedElement = ann.annotationType();
145
		try {
146
			return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
147 148 149
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
150
			handleIntrospectionFailure(annotatedElement, ex);
151 152
			return null;
		}
153 154
	}

155
	/**
C
Chris Beams 已提交
156
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
157 158
	 * {@link AnnotatedElement}, where the annotation is either <em>present</em> or
	 * <em>meta-present</em> on the {@code AnnotatedElement}.
159 160 161
	 * <p>Note that this method supports only a single level of meta-annotations.
	 * For support for arbitrary levels of meta-annotations, use
	 * {@link #findAnnotation(AnnotatedElement, Class)} instead.
162
	 * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation
J
Juergen Hoeller 已提交
163
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
164
	 * @return the first matching annotation, or {@code null} if not found
C
Chris Beams 已提交
165
	 * @since 3.1
166
	 */
167
	public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
168
		try {
169 170
			A annotation = annotatedElement.getAnnotation(annotationType);
			if (annotation == null) {
171
				for (Annotation metaAnn : annotatedElement.getAnnotations()) {
172 173
					annotation = metaAnn.annotationType().getAnnotation(annotationType);
					if (annotation != null) {
174 175
						break;
					}
176 177
				}
			}
178
			return synthesizeAnnotation(annotation, annotatedElement);
179 180 181
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
182
			handleIntrospectionFailure(annotatedElement, ex);
183
			return null;
184 185 186
		}
	}

187
	/**
188
	 * Get a single {@link Annotation} of {@code annotationType} from the
189 190
	 * supplied {@link Method}, where the annotation is either <em>present</em>
	 * or <em>meta-present</em> on the method.
191
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
192 193 194
	 * <p>Note that this method supports only a single level of meta-annotations.
	 * For support for arbitrary levels of meta-annotations, use
	 * {@link #findAnnotation(Method, Class)} instead.
195 196
	 * @param method the method to look for annotations on
	 * @param annotationType the annotation type to look for
197
	 * @return the first matching annotation, or {@code null} if not found
198 199 200 201 202 203 204 205 206 207 208 209
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
	 * @see #getAnnotation(AnnotatedElement, Class)
	 */
	public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
		Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
		return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
	}

	/**
	 * Get all {@link Annotation Annotations} that are <em>present</em> on the
	 * supplied {@link AnnotatedElement}.
	 * <p>Meta-annotations will <em>not</em> be searched.
210
	 * @param annotatedElement the Method, Constructor or Field to retrieve annotations from
211 212 213
	 * @return the annotations found, an empty array, or {@code null} if not
	 * resolvable (e.g. because nested Class values in annotation attributes
	 * failed to resolve at runtime)
214 215 216 217 218 219 220 221
	 * @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...
222
			handleIntrospectionFailure(annotatedElement, ex);
223
		}
224
		return null;
225 226
	}

A
Arjen Poutsma 已提交
227
	/**
228 229
	 * Get all {@link Annotation Annotations} that are <em>present</em on the
	 * supplied {@link Method}.
A
Arjen Poutsma 已提交
230
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
231
	 * <p>Meta-annotations will <em>not</em> be searched.
232
	 * @param method the Method to retrieve annotations from
233 234 235
	 * @return the annotations found, an empty array, or {@code null} if not
	 * resolvable (e.g. because nested Class values in annotation attributes
	 * failed to resolve at runtime)
A
Arjen Poutsma 已提交
236
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
237
	 * @see AnnotatedElement#getAnnotations()
A
Arjen Poutsma 已提交
238 239
	 */
	public static Annotation[] getAnnotations(Method method) {
240 241 242 243 244
		try {
			return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
245
			handleIntrospectionFailure(method, ex);
246
		}
247
		return null;
A
Arjen Poutsma 已提交
248 249 250
	}

	/**
251 252 253 254 255
	 * Get the <em>repeatable</em> {@link Annotation}s of {@code annotationType}
	 * from the supplied {@link Method}, where such annotations are either
	 * <em>present</em> or <em>meta-present</em> on the method.
	 * <p>Handles both single annotations and annotations nested within a
	 * <em>containing annotation</em>.
256
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
257
	 * <p>Meta-annotations will be searched if the annotation is not
258 259 260 261
	 * <em>present</em> on the supplied method.
	 * @param method the method to look for annotations on; never {@code null}
	 * @param containerAnnotationType the type of the container that holds the
	 * annotations; may be {@code null} if a container is not supported
J
Juergen Hoeller 已提交
262
	 * @param annotationType the annotation type to look for
263
	 * @return the annotations found or an empty set; never {@code null}
264
	 * @since 4.0
J
Juergen Hoeller 已提交
265
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
266
	 * @see java.lang.annotation.Repeatable
267 268 269
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
270

271
		Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
J
Juergen Hoeller 已提交
272
		return getRepeatableAnnotation((AnnotatedElement) resolvedMethod, containerAnnotationType, annotationType);
273 274 275
	}

	/**
276 277 278 279 280
	 * Get the <em>repeatable</em> {@link Annotation}s of {@code annotationType}
	 * from the supplied {@link AnnotatedElement}, where such annotations are
	 * either <em>present</em> or <em>meta-present</em> on the element.
	 * <p>Handles both single annotations and annotations nested within a
	 * <em>containing annotation</em>.
281
	 * <p>Meta-annotations will be searched if the annotation is not
282 283 284 285
	 * <em>present</em> on the supplied element.
	 * @param annotatedElement the element to look for annotations on; never {@code null}
	 * @param containerAnnotationType the type of the container that holds the
	 * annotations; may be {@code null} if a container is not supported
J
Juergen Hoeller 已提交
286
	 * @param annotationType the annotation type to look for
287
	 * @return the annotations found or an empty set; never {@code null}
288
	 * @since 4.0
289
	 * @see java.lang.annotation.Repeatable
290 291 292
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
293

294 295 296 297
		try {
			if (annotatedElement.getAnnotations().length > 0) {
				return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement);
			}
298
		}
299 300
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
301
			handleIntrospectionFailure(annotatedElement, ex);
302 303
		}
		return Collections.emptySet();
304 305
	}

A
Arjen Poutsma 已提交
306
	/**
307 308 309 310 311 312 313 314 315 316 317 318
	 * Find a single {@link Annotation} of {@code annotationType} on the
	 * supplied {@link AnnotatedElement}.
	 * <p>Meta-annotations will be searched if the annotation is not
	 * <em>directly present</em> on the supplied element.
	 * <p><strong>Warning</strong>: this method operates generically on
	 * annotated elements. In other words, this method does not execute
	 * specialized search algorithms for classes or methods. If you require
	 * the more specific semantics of {@link #findAnnotation(Class, Class)}
	 * or {@link #findAnnotation(Method, Class)}, invoke one of those methods
	 * instead.
	 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
319
	 * @return the first matching annotation, or {@code null} if not found
320 321 322 323 324
	 * @since 4.2
	 */
	public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
		// Do NOT store result in the findAnnotationCache since doing so could break
		// findAnnotation(Class, Class) and findAnnotation(Method, Class).
325 326
		return synthesizeAnnotation(findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()),
			annotatedElement);
327 328 329 330 331 332 333 334 335
	}

	/**
	 * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)}
	 * avoiding endless recursion by tracking which annotations have already
	 * been <em>visited</em>.
	 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
	 * @param visited the set of annotations that have already been visited
336
	 * @return the first matching annotation, or {@code null} if not found
337 338 339
	 * @since 4.2
	 */
	@SuppressWarnings("unchecked")
340
	private static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
341 342 343 344 345
		Assert.notNull(annotatedElement, "AnnotatedElement must not be null");
		try {
			Annotation[] anns = annotatedElement.getDeclaredAnnotations();
			for (Annotation ann : anns) {
				if (ann.annotationType().equals(annotationType)) {
346
					return (A) ann;
347 348 349 350
				}
			}
			for (Annotation ann : anns) {
				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
351
					A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited);
352 353 354 355 356 357 358 359
					if (annotation != null) {
						return annotation;
					}
				}
			}
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
360
			handleIntrospectionFailure(annotatedElement, ex);
361 362 363 364 365 366
		}
		return null;
	}

	/**
	 * Find a single {@link Annotation} of {@code annotationType} on the supplied
J
Juergen Hoeller 已提交
367
	 * {@link Method}, traversing its super methods (i.e., from superclasses and
368 369
	 * interfaces) if the annotation is not <em>directly present</em> on the given
	 * method itself.
370 371 372
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
	 * <p>Meta-annotations will be searched if the annotation is not
	 * <em>directly present</em> on the method.
S
Sam Brannen 已提交
373 374
	 * <p>Annotations on methods are not inherited by default, so we need to handle
	 * this explicitly.
A
Arjen Poutsma 已提交
375
	 * @param method the method to look for annotations on
J
Juergen Hoeller 已提交
376
	 * @param annotationType the annotation type to look for
377
	 * @return the first matching annotation, or {@code null} if not found
378
	 * @see #getAnnotation(Method, Class)
A
Arjen Poutsma 已提交
379
	 */
380
	@SuppressWarnings("unchecked")
A
Arjen Poutsma 已提交
381
	public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
382 383
		AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
		A result = (A) findAnnotationCache.get(cacheKey);
384

385
		if (result == null) {
386 387 388
			Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
			result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);

389
			if (result == null) {
390
				result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces());
A
Arjen Poutsma 已提交
391
			}
392 393

			Class<?> clazz = method.getDeclaringClass();
394 395
			while (result == null) {
				clazz = clazz.getSuperclass();
396
				if (clazz == null || Object.class == clazz) {
397 398 399 400
					break;
				}
				try {
					Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
401 402
					Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod);
					result = findAnnotation((AnnotatedElement) resolvedEquivalentMethod, annotationType);
403 404 405 406 407 408 409
				}
				catch (NoSuchMethodException ex) {
					// No equivalent method found
				}
				if (result == null) {
					result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
				}
410
			}
411

412 413 414
			if (result != null) {
				findAnnotationCache.put(cacheKey, result);
			}
A
Arjen Poutsma 已提交
415
		}
416

417
		return synthesizeAnnotation(result, method);
A
Arjen Poutsma 已提交
418 419
	}

420
	private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
421
		A annotation = null;
J
Juergen Hoeller 已提交
422
		for (Class<?> iface : ifcs) {
423 424 425 426 427 428 429 430 431 432 433
			if (isInterfaceWithAnnotatedMethods(iface)) {
				try {
					Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
					annotation = getAnnotation(equivalentMethod, annotationType);
				}
				catch (NoSuchMethodException ex) {
					// Skip this interface - it doesn't have the method...
				}
				if (annotation != null) {
					break;
				}
434
			}
435 436 437 438
		}
		return annotation;
	}

439
	static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
440 441
		Boolean flag = annotatedInterfaceCache.get(iface);
		if (flag != null) {
442
			return flag.booleanValue();
443
		}
444
		Boolean found = Boolean.FALSE;
445 446 447
		for (Method ifcMethod : iface.getMethods()) {
			try {
				if (ifcMethod.getAnnotations().length > 0) {
448
					found = Boolean.TRUE;
449
					break;
450
				}
451 452 453
			}
			catch (Exception ex) {
				// Assuming nested Class values not resolvable within annotation attributes...
454
				handleIntrospectionFailure(ifcMethod, ex);
455 456
			}
		}
457
		annotatedInterfaceCache.put(iface, found);
458
		return found.booleanValue();
459 460
	}

A
Arjen Poutsma 已提交
461
	/**
462 463
	 * Find a single {@link Annotation} of {@code annotationType} on the
	 * supplied {@link Class}, traversing its interfaces, annotations, and
464 465
	 * superclasses if the annotation is not <em>directly present</em> on
	 * the given class itself.
S
Sam Brannen 已提交
466
	 * <p>This method explicitly handles class-level annotations which are not
467 468
	 * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
	 * as meta-annotations and annotations on interfaces</em>.
S
Sam Brannen 已提交
469 470
	 * <p>The algorithm operates as follows:
	 * <ol>
471 472
	 * <li>Search for the annotation on the given class and return it if found.
	 * <li>Recursively search through all annotations that the given class declares.
473
	 * <li>Recursively search through all interfaces that the given class declares.
474
	 * <li>Recursively search through the superclass hierarchy of the given class.
S
Sam Brannen 已提交
475
	 * </ol>
476 477 478
	 * <p>Note: in this context, the term <em>recursively</em> means that the search
	 * process continues by returning to step #1 with the current interface,
	 * annotation, or superclass as the class to look for annotations on.
A
Arjen Poutsma 已提交
479
	 * @param clazz the class to look for annotations on
480
	 * @param annotationType the type of annotation to look for
481
	 * @return the first matching annotation, or {@code null} if not found
A
Arjen Poutsma 已提交
482
	 */
483
	@SuppressWarnings("unchecked")
A
Arjen Poutsma 已提交
484
	public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
485 486 487 488 489 490 491 492
		AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);
		A result = (A) findAnnotationCache.get(cacheKey);
		if (result == null) {
			result = findAnnotation(clazz, annotationType, new HashSet<Annotation>());
			if (result != null) {
				findAnnotationCache.put(cacheKey, result);
			}
		}
493
		return synthesizeAnnotation(result, clazz);
494 495 496 497 498
	}

	/**
	 * Perform the search algorithm for {@link #findAnnotation(Class, Class)},
	 * avoiding endless recursion by tracking which annotations have already
S
Sam Brannen 已提交
499
	 * been <em>visited</em>.
500 501
	 * @param clazz the class to look for annotations on
	 * @param annotationType the type of annotation to look for
S
Sam Brannen 已提交
502
	 * @param visited the set of annotations that have already been visited
503
	 * @return the first matching annotation, or {@code null} if not found
504
	 */
505
	@SuppressWarnings("unchecked")
J
Juergen Hoeller 已提交
506
	private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
A
Arjen Poutsma 已提交
507
		Assert.notNull(clazz, "Class must not be null");
508 509 510 511 512 513 514

		try {
			Annotation[] anns = clazz.getDeclaredAnnotations();
			for (Annotation ann : anns) {
				if (ann.annotationType().equals(annotationType)) {
					return (A) ann;
				}
515
			}
516 517 518 519 520 521
			for (Annotation ann : anns) {
				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
					A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
					if (annotation != null) {
						return annotation;
					}
522
				}
523
			}
A
Arjen Poutsma 已提交
524
		}
525 526
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
527
			handleIntrospectionFailure(clazz, ex);
528 529 530
			return null;
		}

A
Arjen Poutsma 已提交
531
		for (Class<?> ifc : clazz.getInterfaces()) {
532
			A annotation = findAnnotation(ifc, annotationType, visited);
A
Arjen Poutsma 已提交
533 534 535 536
			if (annotation != null) {
				return annotation;
			}
		}
537

538
		Class<?> superclass = clazz.getSuperclass();
539
		if (superclass == null || Object.class == superclass) {
A
Arjen Poutsma 已提交
540 541
			return null;
		}
S
Sam Brannen 已提交
542
		return findAnnotation(superclass, annotationType, visited);
A
Arjen Poutsma 已提交
543 544 545
	}

	/**
546 547 548 549 550 551 552
	 * Find the first {@link Class} in the inheritance hierarchy of the
	 * specified {@code clazz} (including the specified {@code clazz} itself)
	 * on which an annotation of the specified {@code annotationType} is
	 * <em>directly present</em>.
	 * <p>If the supplied {@code clazz} is an interface, only the interface
	 * itself will be checked; the inheritance hierarchy for interfaces will
	 * not be traversed.
553
	 * <p>Meta-annotations will <em>not</em> be searched.
554 555 556 557 558 559 560 561
	 * <p>The standard {@link Class} API does not provide a mechanism for
	 * determining which class in an inheritance hierarchy actually declares
	 * an {@link Annotation}, so we need to handle this explicitly.
	 * @param annotationType the annotation type to look for
	 * @param clazz the class to check for the annotation on (may be {@code null})
	 * @return the first {@link Class} in the inheritance hierarchy that
	 * declares an annotation of the specified {@code annotationType}, or
	 * {@code null} if not found
A
Arjen Poutsma 已提交
562 563
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
564 565
	 * @see #findAnnotationDeclaringClassForTypes(List, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
A
Arjen Poutsma 已提交
566 567 568
	 */
	public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
		Assert.notNull(annotationType, "Annotation type must not be null");
569
		if (clazz == null || Object.class == clazz) {
A
Arjen Poutsma 已提交
570 571
			return null;
		}
572 573 574 575
		if (isAnnotationDeclaredLocally(annotationType, clazz)) {
			return clazz;
		}
		return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
576 577 578
	}

	/**
579 580 581 582 583 584 585
	 * Find the first {@link Class} in the inheritance hierarchy of the
	 * specified {@code clazz} (including the specified {@code clazz} itself)
	 * on which at least one of the specified {@code annotationTypes} is
	 * <em>directly present</em>.
	 * <p>If the supplied {@code clazz} is an interface, only the interface
	 * itself will be checked; the inheritance hierarchy for interfaces will
	 * not be traversed.
586
	 * <p>Meta-annotations will <em>not</em> be searched.
587 588 589 590 591 592 593 594
	 * <p>The standard {@link Class} API does not provide a mechanism for
	 * determining which class in an inheritance hierarchy actually declares
	 * one of several candidate {@linkplain Annotation annotations}, so we
	 * need to handle this explicitly.
	 * @param annotationTypes the annotation types to look for
	 * @param clazz the class to check for the annotations on, or {@code null}
	 * @return the first {@link Class} in the inheritance hierarchy that
	 * declares an annotation of at least one of the specified
595
	 * {@code annotationTypes}, or {@code null} if not found
596
	 * @since 3.2.2
597 598 599 600 601
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
	 * @see #findAnnotationDeclaringClass(Class, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
	 */
602
	public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) {
603
		Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");
604
		if (clazz == null || Object.class == clazz) {
605 606 607 608 609 610 611 612
			return null;
		}
		for (Class<? extends Annotation> annotationType : annotationTypes) {
			if (isAnnotationDeclaredLocally(annotationType, clazz)) {
				return clazz;
			}
		}
		return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
A
Arjen Poutsma 已提交
613 614 615
	}

	/**
616 617 618 619
	 * Determine whether an annotation of the specified {@code annotationType}
	 * is declared locally (i.e., <em>directly present</em>) on the supplied
	 * {@code clazz}.
	 * <p>The supplied {@link Class} may represent any type.
620
	 * <p>Meta-annotations will <em>not</em> be searched.
621 622 623
	 * <p>Note: This method does <strong>not</strong> determine if the annotation
	 * is {@linkplain java.lang.annotation.Inherited inherited}. For greater
	 * clarity regarding inherited annotations, consider using
S
Sam Brannen 已提交
624
	 * {@link #isAnnotationInherited(Class, Class)} instead.
625 626
	 * @param annotationType the annotation type to look for
	 * @param clazz the class to check for the annotation on
627
	 * @return {@code true} if an annotation of the specified {@code annotationType}
628
	 * is <em>directly present</em>
629 630
	 * @see java.lang.Class#getDeclaredAnnotations()
	 * @see java.lang.Class#getDeclaredAnnotation(Class)
A
Arjen Poutsma 已提交
631 632 633 634 635
	 * @see #isAnnotationInherited(Class, Class)
	 */
	public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {
		Assert.notNull(annotationType, "Annotation type must not be null");
		Assert.notNull(clazz, "Class must not be null");
636
		try {
637 638
			for (Annotation ann : clazz.getDeclaredAnnotations()) {
				if (ann.annotationType().equals(annotationType)) {
639
					return true;
640
				}
A
Arjen Poutsma 已提交
641 642
			}
		}
643 644
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
645
			handleIntrospectionFailure(clazz, ex);
646
		}
647
		return false;
A
Arjen Poutsma 已提交
648 649 650
	}

	/**
651 652 653 654
	 * Determine whether an annotation of the specified {@code annotationType}
	 * is <em>present</em> on the supplied {@code clazz} and is
	 * {@linkplain java.lang.annotation.Inherited inherited} (i.e., not
	 * <em>directly present</em>).
655
	 * <p>Meta-annotations will <em>not</em> be searched.
656 657 658 659 660 661 662 663 664 665
	 * <p>If the supplied {@code clazz} is an interface, only the interface
	 * itself will be checked. In accordance with standard meta-annotation
	 * semantics in Java, the inheritance hierarchy for interfaces will not
	 * be traversed. See the {@linkplain java.lang.annotation.Inherited Javadoc}
	 * for the {@code @Inherited} meta-annotation for further details regarding
	 * annotation inheritance.
	 * @param annotationType the annotation type to look for
	 * @param clazz the class to check for the annotation on
	 * @return {@code true} if an annotation of the specified {@code annotationType}
	 * is <em>present</em> and <em>inherited</em>
A
Arjen Poutsma 已提交
666 667 668 669 670 671 672 673 674
	 * @see Class#isAnnotationPresent(Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
	 */
	public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {
		Assert.notNull(annotationType, "Annotation type must not be null");
		Assert.notNull(clazz, "Class must not be null");
		return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));
	}

675
	/**
J
Juergen Hoeller 已提交
676
	 * Determine if the supplied {@link Annotation} is defined in the core JDK
677
	 * {@code java.lang.annotation} package.
J
Juergen Hoeller 已提交
678
	 * @param annotation the annotation to check (never {@code null})
679 680 681 682
	 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
	 */
	public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
		Assert.notNull(annotation, "Annotation must not be null");
683 684 685 686 687 688 689 690 691 692 693 694 695
		return isInJavaLangAnnotationPackage(annotation.annotationType().getName());
	}

	/**
	 * Determine if the {@link Annotation} with the supplied name is defined
	 * in the core JDK {@code java.lang.annotation} package.
	 * @param annotationType the name of the annotation type to check (never {@code null} or empty)
	 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
	 * @since 4.2
	 */
	public static boolean isInJavaLangAnnotationPackage(String annotationType) {
		Assert.hasText(annotationType, "annotationType must not be null or empty");
		return annotationType.startsWith("java.lang.annotation");
696 697
	}

A
Arjen Poutsma 已提交
698
	/**
J
Juergen Hoeller 已提交
699
	 * Retrieve the given annotation's attributes as a {@link Map}, preserving all
700 701 702 703
	 * attribute types.
	 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)}
	 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters
	 * set to {@code false}.
J
Juergen Hoeller 已提交
704 705
	 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
	 * However, the {@code Map} signature has been preserved for binary compatibility.
A
Arjen Poutsma 已提交
706
	 * @param annotation the annotation to retrieve the attributes for
J
Juergen Hoeller 已提交
707
	 * @return the Map of annotation attributes, with attribute names as keys and
708 709
	 * corresponding attribute values as values; never {@code null}
	 * @see #getAnnotationAttributes(Annotation, boolean, boolean)
710
	 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
A
Arjen Poutsma 已提交
711 712
	 */
	public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
713
		return getAnnotationAttributes(null, annotation);
714 715 716
	}

	/**
717 718 719
	 * Retrieve the given annotation's attributes as a {@link Map}.
	 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)}
	 * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}.
J
Juergen Hoeller 已提交
720 721
	 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
	 * However, the {@code Map} signature has been preserved for binary compatibility.
722
	 * @param annotation the annotation to retrieve the attributes for
723
	 * @param classValuesAsString whether to turn Class references into Strings (for
S
Sam Brannen 已提交
724
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
J
Juergen Hoeller 已提交
725
	 * or to preserve them as Class references
J
Juergen Hoeller 已提交
726
	 * @return the Map of annotation attributes, with attribute names as keys and
727 728
	 * corresponding attribute values as values; never {@code null}
	 * @see #getAnnotationAttributes(Annotation, boolean, boolean)
729
	 */
730
	public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
731 732 733 734
		return getAnnotationAttributes(annotation, classValuesAsString, false);
	}

	/**
735
	 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
J
Juergen Hoeller 已提交
736 737
	 * <p>This method provides fully recursive annotation reading capabilities on par with
	 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
738
	 * @param annotation the annotation to retrieve the attributes for
739
	 * @param classValuesAsString whether to convert Class references into Strings (for
S
Sam Brannen 已提交
740
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
J
Juergen Hoeller 已提交
741
	 * or to preserve them as Class references
742 743
	 * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
S
Sam Brannen 已提交
744
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
745 746
	 * Annotation instances
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
747
	 * and corresponding attribute values as values; never {@code null}
748 749
	 * @since 3.1.1
	 */
750 751
	public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
			boolean nestedAnnotationsAsMap) {
752 753
		return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap);
	}
754

755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
	/**
	 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
	 * <p>Equivalent to calling {@link #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)}
	 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters
	 * set to {@code false}.
	 * @param annotation the annotation to retrieve the attributes for
	 * @return the Map of annotation attributes, with attribute names as keys and
	 * corresponding attribute values as values; never {@code null}
	 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)
	 * @since 4.2
	 */
	public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation) {
		return getAnnotationAttributes(annotatedElement, annotation, false, false);
	}

	/**
	 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
	 * <p>This method provides fully recursive annotation reading capabilities on par with
	 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
	 * @param annotatedElement the element that is annotated with the supplied annotation,
	 * used for contextual logging; may be {@code null} if unknown
	 * @param annotation the annotation to retrieve the attributes for
	 * @param classValuesAsString whether to convert Class references into Strings (for
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
	 * or to preserve them as Class references
	 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
	 * Annotation instances
	 * @param defaultValuesAsPlaceholder whether to replace default values with
	 * {@link #DEFAULT_VALUE_PLACEHOLDER} or leave them as is
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values; never {@code null}
	 * @since 4.2
	 */
	public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement,
			Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {

		return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap,
			false, true);
	}

	/**
	 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map.
	 *
	 * <p>This method provides fully recursive annotation reading capabilities on par with
	 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
	 *
	 * @param annotatedElement the element that is annotated with the supplied annotation,
	 * used for contextual logging; may be {@code null} if unknown
	 * @param annotation the annotation to retrieve the attributes for
	 * @param classValuesAsString whether to convert Class references into Strings (for
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
	 * or to preserve them as Class references
	 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
	 * Annotation instances
	 * @param defaultValuesAsPlaceholder whether to replace default values with
	 * {@link #DEFAULT_VALUE_PLACEHOLDER} or leave them as is
	 * @param synthesizeAnnotation whether or not the annotation should be
	 * {@linkplain #synthesizeAnnotation synthesized} before processing
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values; never {@code null}
	 * @since 4.2
	 */
	static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement,
			Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap,
			boolean defaultValuesAsPlaceholder, boolean synthesizeAnnotation) {

		if (synthesizeAnnotation) {
826
			annotation = synthesizeAnnotation(annotation, annotatedElement);
827 828 829 830 831 832 833 834 835 836 837 838 839
		}

		Class<? extends Annotation> annotationType = annotation.annotationType();
		AnnotationAttributes attrs = new AnnotationAttributes(annotationType);
		for (Method method : getAttributeMethods(annotationType)) {
			try {
				Object value = method.invoke(annotation);

				Object defaultValue = method.getDefaultValue();
				if (defaultValuesAsPlaceholder && (defaultValue != null)) {
					if (ObjectUtils.nullSafeEquals(value, defaultValue)) {
						value = DEFAULT_VALUE_PLACEHOLDER;
					}
A
Arjen Poutsma 已提交
840
				}
841 842 843 844 845 846 847 848

				attrs.put(method.getName(),
					adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap));
			}
			catch (Exception ex) {
				if (ex instanceof InvocationTargetException) {
					Throwable targetException = ((InvocationTargetException) ex).getTargetException();
					rethrowAnnotationConfigurationException(targetException);
A
Arjen Poutsma 已提交
849
				}
850
				throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
A
Arjen Poutsma 已提交
851 852 853 854 855
			}
		}
		return attrs;
	}

856 857
	/**
	 * Adapt the given value according to the given class and nested annotation settings.
858
	 * <p>Nested annotations will be
859
	 * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}.
860 861
	 * @param annotatedElement the element that is annotated, used for contextual
	 * logging; may be {@code null} if unknown
862 863
	 * @param value the annotation attribute value
	 * @param classValuesAsString whether to turn Class references into Strings (for
S
Sam Brannen 已提交
864
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
865 866 867
	 * or to preserve them as Class references
	 * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
S
Sam Brannen 已提交
868
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
869 870 871
	 * Annotation instances
	 * @return the adapted value, or the original value if no adaptation is needed
	 */
872 873 874
	static Object adaptValue(AnnotatedElement annotatedElement, Object value, boolean classValuesAsString,
			boolean nestedAnnotationsAsMap) {

875 876
		if (classValuesAsString) {
			if (value instanceof Class) {
877
				return ((Class<?>) value).getName();
878 879
			}
			else if (value instanceof Class[]) {
880
				Class<?>[] clazzArray = (Class<?>[]) value;
881
				String[] classNames = new String[clazzArray.length];
882
				for (int i = 0; i < clazzArray.length; i++) {
883
					classNames[i] = clazzArray[i].getName();
884
				}
885
				return classNames;
886 887
			}
		}
888 889 890 891 892 893 894 895 896

		if (value instanceof Annotation) {
			Annotation annotation = (Annotation) value;

			if (nestedAnnotationsAsMap) {
				return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString,
					nestedAnnotationsAsMap);
			}
			else {
897
				return synthesizeAnnotation(annotation, annotatedElement);
898 899
			}
		}
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914

		if (value instanceof Annotation[]) {
			Annotation[] annotations = (Annotation[]) value;

			if (nestedAnnotationsAsMap) {
				AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[annotations.length];
				for (int i = 0; i < annotations.length; i++) {
					mappedAnnotations[i] = getAnnotationAttributes(annotatedElement, annotations[i],
						classValuesAsString, nestedAnnotationsAsMap);
				}
				return mappedAnnotations;
			}
			else {
				Annotation[] synthesizedAnnotations = new Annotation[annotations.length];
				for (int i = 0; i < annotations.length; i++) {
915
					synthesizedAnnotations[i] = synthesizeAnnotation(annotations[i], annotatedElement);
916 917 918
				}
				return synthesizedAnnotations;
			}
919
		}
920 921 922

		// Fallback
		return value;
923 924
	}

A
Arjen Poutsma 已提交
925
	/**
926
	 * Retrieve the <em>value</em> of the {@code value} attribute of a
J
Juergen Hoeller 已提交
927
	 * single-element Annotation, given an annotation instance.
A
Arjen Poutsma 已提交
928
	 * @param annotation the annotation instance from which to retrieve the value
929
	 * @return the attribute value, or {@code null} if not found
A
Arjen Poutsma 已提交
930 931 932 933 934 935 936
	 * @see #getValue(Annotation, String)
	 */
	public static Object getValue(Annotation annotation) {
		return getValue(annotation, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
937
	 * Retrieve the <em>value</em> of a named attribute, given an annotation instance.
A
Arjen Poutsma 已提交
938 939
	 * @param annotation the annotation instance from which to retrieve the value
	 * @param attributeName the name of the attribute value to retrieve
940
	 * @return the attribute value, or {@code null} if not found
J
Juergen Hoeller 已提交
941
	 * @see #getValue(Annotation)
A
Arjen Poutsma 已提交
942 943
	 */
	public static Object getValue(Annotation annotation, String attributeName) {
944
		if (annotation == null || !StringUtils.hasText(attributeName)) {
945 946
			return null;
		}
A
Arjen Poutsma 已提交
947
		try {
J
Juergen Hoeller 已提交
948
			Method method = annotation.annotationType().getDeclaredMethod(attributeName);
949
			ReflectionUtils.makeAccessible(method);
A
Arjen Poutsma 已提交
950 951 952 953 954 955 956 957
			return method.invoke(annotation);
		}
		catch (Exception ex) {
			return null;
		}
	}

	/**
958
	 * Retrieve the <em>default value</em> of the {@code value} attribute
J
Juergen Hoeller 已提交
959 960
	 * of a single-element Annotation, given an annotation instance.
	 * @param annotation the annotation instance from which to retrieve the default value
961
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
962 963 964 965 966 967 968
	 * @see #getDefaultValue(Annotation, String)
	 */
	public static Object getDefaultValue(Annotation annotation) {
		return getDefaultValue(annotation, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
969
	 * Retrieve the <em>default value</em> of a named attribute, given an annotation instance.
J
Juergen Hoeller 已提交
970
	 * @param annotation the annotation instance from which to retrieve the default value
A
Arjen Poutsma 已提交
971
	 * @param attributeName the name of the attribute value to retrieve
972
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
973 974 975
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Annotation annotation, String attributeName) {
976 977 978
		if (annotation == null) {
			return null;
		}
A
Arjen Poutsma 已提交
979 980 981 982
		return getDefaultValue(annotation.annotationType(), attributeName);
	}

	/**
983
	 * Retrieve the <em>default value</em> of the {@code value} attribute
J
Juergen Hoeller 已提交
984 985
	 * of a single-element Annotation, given the {@link Class annotation type}.
	 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
986
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
987 988 989 990 991 992 993
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
		return getDefaultValue(annotationType, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
994 995
	 * Retrieve the <em>default value</em> of a named attribute, given the
	 * {@link Class annotation type}.
J
Juergen Hoeller 已提交
996
	 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
A
Arjen Poutsma 已提交
997
	 * @param attributeName the name of the attribute value to retrieve.
998
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
999 1000 1001
	 * @see #getDefaultValue(Annotation, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
1002
		if (annotationType == null || !StringUtils.hasText(attributeName)) {
1003 1004
			return null;
		}
A
Arjen Poutsma 已提交
1005
		try {
J
Juergen Hoeller 已提交
1006
			return annotationType.getDeclaredMethod(attributeName).getDefaultValue();
A
Arjen Poutsma 已提交
1007 1008 1009 1010 1011 1012
		}
		catch (Exception ex) {
			return null;
		}
	}

1013
	/**
1014 1015 1016 1017
	 * <em>Synthesize</em> the supplied {@code annotation} by wrapping it in
	 * a dynamic proxy that transparently enforces <em>attribute alias</em>
	 * semantics for annotation attributes that are annotated with
	 * {@link AliasFor @AliasFor}.
1018 1019
	 *
	 * @param annotation the annotation to synthesize
1020 1021 1022 1023 1024
	 * @return the synthesized annotation, if the supplied annotation is
	 * <em>synthesizable</em>; {@code null} if the supplied annotation is
	 * {@code null}; otherwise, the supplied annotation unmodified
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
1025
	 * @since 4.2
1026
	 * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1027
	 */
1028 1029
	static <A extends Annotation> A synthesizeAnnotation(A annotation) {
		return synthesizeAnnotation(annotation, null);
1030 1031 1032
	}

	/**
1033 1034 1035 1036
	 * <em>Synthesize</em> the supplied {@code annotation} by wrapping it in
	 * a dynamic proxy that transparently enforces <em>attribute alias</em>
	 * semantics for annotation attributes that are annotated with
	 * {@link AliasFor @AliasFor}.
1037 1038
	 *
	 * @param annotation the annotation to synthesize
1039 1040 1041 1042 1043 1044 1045
	 * @param annotatedElement the element that is annotated with the supplied
	 * annotation; may be {@code null} if unknown
	 * @return the synthesized annotation, if the supplied annotation is
	 * <em>synthesizable</em>; {@code null} if the supplied annotation is
	 * {@code null}; otherwise, the supplied annotation unmodified
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
1046 1047 1048
	 * @since 4.2
	 */
	@SuppressWarnings("unchecked")
1049
	public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) {
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
		if (annotation == null) {
			return null;
		}
		if (annotation instanceof SynthesizedAnnotation) {
			return annotation;
		}

		Class<? extends Annotation> annotationType = annotation.annotationType();

		// No need to synthesize?
		if (!isSynthesizable(annotationType)) {
			return annotation;
		}

1064 1065
		InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(annotatedElement, annotation,
			getAttributeAliasMap(annotationType));
1066 1067 1068 1069 1070 1071
		A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
			(Class<A>) annotationType, SynthesizedAnnotation.class }, handler);

		return synthesizedAnnotation;
	}

1072

1073
	/**
1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
	 * Get a map of all attribute alias pairs, declared via {@code @AliasFor}
	 * in the supplied annotation type.
	 *
	 * <p>The map is keyed by attribute name with each value representing
	 * the name of the aliased attribute. For each entry {@code [x, y]} in
	 * the map there will be a corresponding {@code [y, x]} entry in the map.
	 *
	 * <p>An empty return value implies that the annotation does not declare
	 * any attribute aliases.
	 *
	 * @param annotationType the annotation type to find attribute aliases in
	 * @return a map containing attribute alias pairs; never {@code null}
1086 1087
	 * @since 4.2
	 */
1088
	private static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
1089
		if (annotationType == null) {
1090 1091 1092 1093 1094 1095
			return Collections.emptyMap();
		}

		Map<String, String> cachedMap = attributeAliasCache.get(annotationType);
		if (cachedMap != null) {
			return cachedMap;
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
		}

		Map<String, String> map = new HashMap<String, String>();
		for (Method attribute : getAttributeMethods(annotationType)) {
			String attributeName = attribute.getName();
			String aliasedAttributeName = getAliasedAttributeName(attribute);
			if (aliasedAttributeName != null) {
				map.put(attributeName, aliasedAttributeName);
			}
		}
1106 1107 1108

		attributeAliasCache.put(annotationType, map);

1109 1110 1111 1112
		return map;
	}

	/**
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122
	 * Determine if annotations of the supplied {@code annotationType} are
	 * <em>synthesizable</em> (i.e., in need of being wrapped in a dynamic
	 * proxy that provides functionality above that of a standard JDK
	 * annotation).
	 *
	 * <p>Specifically, an annotation is <em>synthesizable</em> if it declares
	 * any attributes that are configured as <em>aliased pairs</em> via
	 * {@link AliasFor @AliasFor} or if any nested annotations used by the
	 * annotation declare such <em>aliased pairs</em>.
	 *
1123
	 * @since 4.2
1124 1125
	 * @see SynthesizedAnnotation
	 * @see SynthesizedAnnotationInvocationHandler
1126 1127 1128 1129
	 */
	@SuppressWarnings("unchecked")
	private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {

1130 1131 1132 1133 1134 1135 1136
		Boolean flag = synthesizableCache.get(annotationType);
		if (flag != null) {
			return flag.booleanValue();
		}

		Boolean synthesizable = Boolean.FALSE;

1137 1138
		for (Method attribute : getAttributeMethods(annotationType)) {
			if (getAliasedAttributeName(attribute) != null) {
1139 1140
				synthesizable = Boolean.TRUE;
				break;
1141 1142 1143 1144 1145 1146 1147
			}

			Class<?> returnType = attribute.getReturnType();

			if (Annotation[].class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType();
				if (isSynthesizable(nestedAnnotationType)) {
1148 1149
					synthesizable = Boolean.TRUE;
					break;
1150 1151 1152 1153 1154
				}
			}
			else if (Annotation.class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
				if (isSynthesizable(nestedAnnotationType)) {
1155 1156
					synthesizable = Boolean.TRUE;
					break;
1157 1158 1159 1160
				}
			}
		}

1161 1162 1163
		synthesizableCache.put(annotationType, synthesizable);

		return synthesizable.booleanValue();
1164
	}
1165

1166
	/**
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
	 * Get the name of the aliased attribute configured via
	 * {@link AliasFor @AliasFor} on the supplied annotation {@code attribute}.
	 *
	 * <p>This method does not resolve aliases in other annotations. In
	 * other words, if {@code @AliasFor} is present on the supplied
	 * {@code attribute} but {@linkplain AliasFor#annotation references an
	 * annotation} other than {@link Annotation}, this method will return
	 * {@code null} immediately.
	 *
	 * @param attribute the attribute to find an alias for
	 * @return the name of the aliased attribute, or {@code null} if not found
	 * @throws IllegalArgumentException if the supplied attribute method is
	 * not from an annotation, or if the supplied target type is {@link Annotation}
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
	 * @see #getAliasedAttributeName(Method, Class)
	 * @since 4.2
	 */
	static String getAliasedAttributeName(Method attribute) {
		return getAliasedAttributeName(attribute, null);
	}

	/**
	 * Get the name of the aliased attribute configured via
	 * {@link AliasFor @AliasFor} on the supplied annotation {@code attribute}.
	 *
	 * @param attribute the attribute to find an alias for
	 * @param targetAnnotationType the type of annotation in which the
	 * aliased attribute is allowed to be declared; {@code null} implies
	 * <em>within the same annotation</em>
	 * @return the name of the aliased attribute, or {@code null} if not found
	 * @throws IllegalArgumentException if the supplied attribute method is
	 * not from an annotation, or if the supplied target type is {@link Annotation}
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
	 * @since 4.2
	 */
	@SuppressWarnings("unchecked")
	static String getAliasedAttributeName(Method attribute, Class<? extends Annotation> targetAnnotationType) {
		Class<?> declaringClass = attribute.getDeclaringClass();
		Assert.isTrue(declaringClass.isAnnotation(), "attribute method must be from an annotation");
		Assert.isTrue(!Annotation.class.equals(targetAnnotationType),
			"targetAnnotationType must not be java.lang.annotation.Annotation");

		AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);

		// Nothing to check
		if (aliasFor == null) {
			return null;
		}

		Class<? extends Annotation> sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
		Class<? extends Annotation> aliasedAnnotationType = aliasFor.annotation();

		boolean searchWithinSameAnnotation = (targetAnnotationType == null);
		boolean sameTargetDeclared = (sourceAnnotationType.equals(aliasedAnnotationType) || Annotation.class.equals(aliasedAnnotationType));

		// Wrong search scope?
		if (searchWithinSameAnnotation && !sameTargetDeclared) {
			return null;
		}

		String attributeName = attribute.getName();
		String aliasedAttributeName = aliasFor.attribute();

		if (!StringUtils.hasText(aliasedAttributeName)) {
			String msg = String.format(
				"@AliasFor declaration on attribute [%s] in annotation [%s] is missing required 'attribute' value.",
				attributeName, sourceAnnotationType.getName());
			throw new AnnotationConfigurationException(msg);
		}

		if (sameTargetDeclared) {
			aliasedAnnotationType = sourceAnnotationType;
		}

		Method aliasedAttribute = null;
		try {
			aliasedAttribute = aliasedAnnotationType.getDeclaredMethod(aliasedAttributeName);
		}
		catch (NoSuchMethodException e) {
			String msg = String.format(
				"Attribute [%s] in annotation [%s] is declared as an @AliasFor nonexistent attribute [%s] in annotation [%s].",
				attributeName, sourceAnnotationType.getName(), aliasedAttributeName, aliasedAnnotationType.getName());
			throw new AnnotationConfigurationException(msg, e);
		}

		if (sameTargetDeclared) {
			AliasFor mirrorAliasFor = aliasedAttribute.getAnnotation(AliasFor.class);
			if (mirrorAliasFor == null) {
				String msg = String.format("Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].",
					aliasedAttributeName, sourceAnnotationType.getName(), attributeName);
				throw new AnnotationConfigurationException(msg);
			}

			String mirrorAliasedAttributeName = mirrorAliasFor.attribute();
			if (!attributeName.equals(mirrorAliasedAttributeName)) {
				String msg = String.format(
					"Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
					aliasedAttributeName, sourceAnnotationType.getName(), attributeName, mirrorAliasedAttributeName);
				throw new AnnotationConfigurationException(msg);
			}
		}

		Class<?> returnType = attribute.getReturnType();
		Class<?> aliasedReturnType = aliasedAttribute.getReturnType();
		if (!returnType.equals(aliasedReturnType)) {
			String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
					+ "and attribute [%s] in annotation [%s] must declare the same return type.", attributeName,
				sourceAnnotationType.getName(), aliasedAttributeName, aliasedAnnotationType.getName());
			throw new AnnotationConfigurationException(msg);
		}

		if (sameTargetDeclared) {
			Object defaultValue = attribute.getDefaultValue();
			Object aliasedDefaultValue = aliasedAttribute.getDefaultValue();

			if ((defaultValue == null) || (aliasedDefaultValue == null)) {
				String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
						+ "and attribute [%s] in annotation [%s] must declare default values.", attributeName,
					sourceAnnotationType.getName(), aliasedAttributeName, aliasedAnnotationType.getName());
				throw new AnnotationConfigurationException(msg);
			}

			if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) {
				String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] "
						+ "and attribute [%s] in annotation [%s] must declare the same default value.", attributeName,
					sourceAnnotationType.getName(), aliasedAttributeName, aliasedAnnotationType.getName());
				throw new AnnotationConfigurationException(msg);
			}
		}

		return aliasedAttributeName;
	}

	/**
1303 1304
	 * Get all methods declared in the supplied {@code annotationType} that
	 * match Java's requirements for annotation <em>attributes</em>.
1305
	 *
1306 1307 1308 1309 1310 1311
	 * <p>All methods in the returned list will be
	 * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}.
	 *
	 * @param annotationType the type in which to search for attribute methods
	 * @return all annotation attribute methods in the specified annotation
	 * type; never {@code null}, though potentially <em>empty</em>
1312 1313 1314 1315 1316 1317
	 * @since 4.2
	 */
	static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
		List<Method> methods = new ArrayList<Method>();
		for (Method method : annotationType.getDeclaredMethods()) {
			if ((method.getParameterTypes().length == 0) && (method.getReturnType() != void.class)) {
1318
				ReflectionUtils.makeAccessible(method);
1319 1320 1321 1322 1323 1324 1325 1326 1327
				methods.add(method);
			}
		}
		return methods;
	}

	/**
	 * TODO Document postProcessAnnotationAttributes().
	 *
1328 1329 1330
	 * @param element the element that is annotated with the supplied annotation,
	 * used for contextual logging; may be {@code null} if unknown
	 * @param attributes the annotation attributes to post-process
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
	 * @since 4.2
	 */
	static void postProcessAnnotationAttributes(AnnotatedElement element, AnnotationAttributes attributes,
			boolean classValuesAsString, boolean nestedAnnotationsAsMap) {

		// Abort?
		if (attributes == null) {
			return;
		}

		Class<? extends Annotation> annotationType = attributes.annotationType();

		// Validate @AliasFor configuration
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
		Map<String, String> aliasMap = getAttributeAliasMap(annotationType);
		Set<String> validated = new HashSet<String>();
		for (String attributeName : aliasMap.keySet()) {
			String aliasedAttributeName = aliasMap.get(attributeName);

			if (validated.add(attributeName) && validated.add(aliasedAttributeName)) {
				Object value = attributes.get(attributeName);
				Object aliasedValue = attributes.get(aliasedAttributeName);

				if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !DEFAULT_VALUE_PLACEHOLDER.equals(value)
						&& !DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
					String elementAsString = (element == null ? "unknown element" : element.toString());
					String msg = String.format(
						"In AnnotationAttributes for annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are "
								+ "declared with values of [%s] and [%s], but only one declaration is permitted.",
						annotationType.getName(), elementAsString, attributeName, aliasedAttributeName,
						ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue));
					throw new AnnotationConfigurationException(msg);
				}
1363

1364 1365 1366 1367 1368 1369 1370 1371
				// Replace default values with aliased values...
				if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) {
					attributes.put(attributeName,
						adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
				}
				if (DEFAULT_VALUE_PLACEHOLDER.equals(aliasedValue)) {
					attributes.put(aliasedAttributeName,
						adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
				}
			}
		}

		for (String attributeName : attributes.keySet()) {
			Object value = attributes.get(attributeName);
			if (DEFAULT_VALUE_PLACEHOLDER.equals(value)) {
				attributes.put(attributeName,
					adaptValue(element, getDefaultValue(annotationType, attributeName), classValuesAsString,
						nestedAnnotationsAsMap));
			}
		}
	}

	/**
	 * <p>If the supplied throwable is an {@link AnnotationConfigurationException},
	 * it will be cast to an {@code AnnotationConfigurationException} and thrown,
	 * allowing it to propagate to the caller.
	 * <p>Otherwise, this method does nothing.
	 * @since 4.2
	 */
	static void rethrowAnnotationConfigurationException(Throwable t) {
		if (t instanceof AnnotationConfigurationException) {
			throw (AnnotationConfigurationException) t;
		}
	}

	/**
	 * Handle the supplied annotation introspection exception.
	 * <p>If the supplied exception is an {@link AnnotationConfigurationException},
	 * it will simply be thrown, allowing it to propagate to the caller, and
	 * nothing will be logged.
	 * <p>Otherwise, this method logs an introspection failure (in particular
	 * {@code TypeNotPresentExceptions}) &mdash; before moving on, pretending
	 * there were no annotations on this specific element.
1407 1408
	 * @param element the element that we tried to introspect annotations on
	 * @param ex the exception that we encountered
1409
	 * @see #rethrowAnnotationConfigurationException
1410
	 */
1411 1412 1413 1414
	static void handleIntrospectionFailure(AnnotatedElement element, Exception ex) {

		rethrowAnnotationConfigurationException(ex);

1415 1416 1417 1418 1419
		Log loggerToUse = logger;
		if (loggerToUse == null) {
			loggerToUse = LogFactory.getLog(AnnotationUtils.class);
			logger = loggerToUse;
		}
1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430
		if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
			// Meta-annotation lookup on an annotation type
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to introspect meta-annotations on [" + element + "]: " + ex);
			}
		}
		else {
			// Direct annotation lookup on regular Class, Method, Field
			if (loggerToUse.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + element + "]: " + ex);
			}
1431 1432 1433 1434
		}
	}


1435
	/**
1436
	 * Cache key for the AnnotatedElement cache.
1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468
	 */
	private static class AnnotationCacheKey {

		private final AnnotatedElement element;

		private final Class<? extends Annotation> annotationType;

		public AnnotationCacheKey(AnnotatedElement element, Class<? extends Annotation> annotationType) {
			this.element = element;
			this.annotationType = annotationType;
		}

		@Override
		public boolean equals(Object other) {
			if (this == other) {
				return true;
			}
			if (!(other instanceof AnnotationCacheKey)) {
				return false;
			}
			AnnotationCacheKey otherKey = (AnnotationCacheKey) other;
			return (this.element.equals(otherKey.element) &&
					ObjectUtils.nullSafeEquals(this.annotationType, otherKey.annotationType));
		}

		@Override
		public int hashCode() {
			return (this.element.hashCode() * 29 + this.annotationType.hashCode());
		}
	}


1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
	private static class AnnotationCollector<A extends Annotation> {

		private final Class<? extends Annotation> containerAnnotationType;

		private final Class<A> annotationType;

		private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>();

		private final Set<A> result = new LinkedHashSet<A>();

J
Juergen Hoeller 已提交
1479
		public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
			this.containerAnnotationType = containerAnnotationType;
			this.annotationType = annotationType;
		}

		public Set<A> getResult(AnnotatedElement element) {
			process(element);
			return Collections.unmodifiableSet(this.result);
		}

		@SuppressWarnings("unchecked")
1490 1491 1492 1493
		private void process(AnnotatedElement element) {
			if (this.visited.add(element)) {
				try {
					for (Annotation ann : element.getAnnotations()) {
1494 1495
						Class<? extends Annotation> currentAnnotationType = ann.annotationType();
						if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
1496
							this.result.add(synthesizeAnnotation((A) ann, element));
1497
						}
1498
						else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
1499
							this.result.addAll(getValue(element, ann));
1500 1501
						}
						else if (!isInJavaLangAnnotationPackage(ann)) {
1502
							process(currentAnnotationType);
1503
						}
1504 1505
					}
				}
1506
				catch (Exception ex) {
1507
					handleIntrospectionFailure(element, ex);
1508
				}
1509 1510 1511 1512
			}
		}

		@SuppressWarnings("unchecked")
1513
		private List<A> getValue(AnnotatedElement element, Annotation annotation) {
1514 1515
			try {
				Method method = annotation.annotationType().getDeclaredMethod("value");
1516
				ReflectionUtils.makeAccessible(method);
1517 1518 1519 1520
				A[] annotations = (A[]) method.invoke(annotation);

				List<A> synthesizedAnnotations = new ArrayList<A>();
				for (A anno : annotations) {
1521
					synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
1522 1523
				}
				return synthesizedAnnotations;
1524 1525
			}
			catch (Exception ex) {
1526 1527
				// Unable to read value from repeating annotation container -> ignore it.
				return Collections.emptyList();
1528 1529 1530
			}
		}
	}
J
Juergen Hoeller 已提交
1531

A
Arjen Poutsma 已提交
1532
}