AnnotationUtils.java 67.5 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

106
	/**
S
Sam Brannen 已提交
107
	 * An object that can be stored in {@link AnnotationAttributes} as a
108 109
	 * placeholder for an attribute's declared default value.
	 */
110
	private static final Object DEFAULT_VALUE_PLACEHOLDER = new String("<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);

S
Sam Brannen 已提交
121
	private static final Map<Class<? extends Annotation>, Map<String, String>> attributeAliasesCache =
122 123
			new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, String>>(256);

124 125 126
	private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache =
			new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256);

127 128
	private static transient Log logger;

J
Juergen Hoeller 已提交
129

130 131
	/**
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
132 133
	 * annotation: either the given annotation itself or a direct meta-annotation
	 * thereof.
134 135 136
	 * <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.
137
	 * @param ann the Annotation to check
J
Juergen Hoeller 已提交
138
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
139
	 * @return the first matching annotation, or {@code null} if not found
140 141 142
	 * @since 4.0
	 */
	@SuppressWarnings("unchecked")
143
	public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType) {
144
		if (annotationType.isInstance(ann)) {
145
			return synthesizeAnnotation((A) ann);
146
		}
147
		Class<? extends Annotation> annotatedElement = ann.annotationType();
148
		try {
149
			return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
150 151 152
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
153
			handleIntrospectionFailure(annotatedElement, ex);
154 155
			return null;
		}
156 157
	}

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

190
	/**
191
	 * Get a single {@link Annotation} of {@code annotationType} from the
192 193
	 * supplied {@link Method}, where the annotation is either <em>present</em>
	 * or <em>meta-present</em> on the method.
194
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
195 196 197
	 * <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.
198 199
	 * @param method the method to look for annotations on
	 * @param annotationType the annotation type to look for
200
	 * @return the first matching annotation, or {@code null} if not found
201 202 203 204 205 206 207 208 209 210 211 212
	 * @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.
213
	 * @param annotatedElement the Method, Constructor or Field to retrieve annotations from
214 215 216
	 * @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)
217 218 219 220 221 222 223 224
	 * @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...
225
			handleIntrospectionFailure(annotatedElement, ex);
226
		}
227
		return null;
228 229
	}

A
Arjen Poutsma 已提交
230
	/**
231 232
	 * Get all {@link Annotation Annotations} that are <em>present</em on the
	 * supplied {@link Method}.
A
Arjen Poutsma 已提交
233
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
234
	 * <p>Meta-annotations will <em>not</em> be searched.
235
	 * @param method the Method to retrieve annotations from
236 237 238
	 * @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 已提交
239
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
240
	 * @see AnnotatedElement#getAnnotations()
A
Arjen Poutsma 已提交
241 242
	 */
	public static Annotation[] getAnnotations(Method method) {
243 244 245 246 247
		try {
			return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
248
			handleIntrospectionFailure(method, ex);
249
		}
250
		return null;
A
Arjen Poutsma 已提交
251 252 253
	}

	/**
254 255 256 257 258
	 * 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>.
259
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
260
	 * <p>Meta-annotations will be searched if the annotation is not
261 262 263 264
	 * <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 已提交
265
	 * @param annotationType the annotation type to look for
266
	 * @return the annotations found or an empty set; never {@code null}
267
	 * @since 4.0
J
Juergen Hoeller 已提交
268
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
269
	 * @see java.lang.annotation.Repeatable
270 271 272
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
273

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

	/**
279 280 281 282 283
	 * 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>.
284
	 * <p>Meta-annotations will be searched if the annotation is not
285 286 287 288
	 * <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 已提交
289
	 * @param annotationType the annotation type to look for
290
	 * @return the annotations found or an empty set; never {@code null}
291
	 * @since 4.0
292
	 * @see java.lang.annotation.Repeatable
293 294 295
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
296

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

A
Arjen Poutsma 已提交
309
	/**
310 311 312 313 314 315 316 317 318 319 320 321
	 * 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
322
	 * @return the first matching annotation, or {@code null} if not found
323 324 325 326 327
	 * @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).
328 329
		return synthesizeAnnotation(findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()),
			annotatedElement);
330 331 332 333 334 335 336 337 338
	}

	/**
	 * 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
339
	 * @return the first matching annotation, or {@code null} if not found
340 341 342
	 * @since 4.2
	 */
	@SuppressWarnings("unchecked")
343
	private static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
344 345 346 347 348
		Assert.notNull(annotatedElement, "AnnotatedElement must not be null");
		try {
			Annotation[] anns = annotatedElement.getDeclaredAnnotations();
			for (Annotation ann : anns) {
				if (ann.annotationType().equals(annotationType)) {
349
					return (A) ann;
350 351 352 353
				}
			}
			for (Annotation ann : anns) {
				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
354
					A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited);
355 356 357 358 359 360 361 362
					if (annotation != null) {
						return annotation;
					}
				}
			}
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
363
			handleIntrospectionFailure(annotatedElement, ex);
364 365 366 367 368 369
		}
		return null;
	}

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

388
		if (result == null) {
389 390 391
			Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
			result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType);

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

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

415 416 417
			if (result != null) {
				findAnnotationCache.put(cacheKey, result);
			}
A
Arjen Poutsma 已提交
418
		}
419

420
		return synthesizeAnnotation(result, method);
A
Arjen Poutsma 已提交
421 422
	}

423
	private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
424
		A annotation = null;
J
Juergen Hoeller 已提交
425
		for (Class<?> iface : ifcs) {
426 427 428 429 430 431 432 433 434 435 436
			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;
				}
437
			}
438 439 440 441
		}
		return annotation;
	}

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

A
Arjen Poutsma 已提交
464
	/**
465 466
	 * Find a single {@link Annotation} of {@code annotationType} on the
	 * supplied {@link Class}, traversing its interfaces, annotations, and
467 468
	 * superclasses if the annotation is not <em>directly present</em> on
	 * the given class itself.
S
Sam Brannen 已提交
469
	 * <p>This method explicitly handles class-level annotations which are not
470 471
	 * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
	 * as meta-annotations and annotations on interfaces</em>.
S
Sam Brannen 已提交
472 473
	 * <p>The algorithm operates as follows:
	 * <ol>
474 475
	 * <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.
476
	 * <li>Recursively search through all interfaces that the given class declares.
477
	 * <li>Recursively search through the superclass hierarchy of the given class.
S
Sam Brannen 已提交
478
	 * </ol>
479 480 481
	 * <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 已提交
482
	 * @param clazz the class to look for annotations on
483
	 * @param annotationType the type of annotation to look for
484
	 * @return the first matching annotation, or {@code null} if not found
A
Arjen Poutsma 已提交
485
	 */
486
	@SuppressWarnings("unchecked")
A
Arjen Poutsma 已提交
487
	public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
488 489 490 491 492 493 494 495
		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);
			}
		}
496
		return synthesizeAnnotation(result, clazz);
497 498 499 500 501
	}

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

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

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

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

	/**
549 550 551 552 553 554 555
	 * 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.
556
	 * <p>Meta-annotations will <em>not</em> be searched.
557 558 559 560 561 562 563 564
	 * <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 已提交
565 566
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
567 568
	 * @see #findAnnotationDeclaringClassForTypes(List, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
A
Arjen Poutsma 已提交
569 570 571
	 */
	public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
		Assert.notNull(annotationType, "Annotation type must not be null");
572
		if (clazz == null || Object.class == clazz) {
A
Arjen Poutsma 已提交
573 574
			return null;
		}
575 576 577 578
		if (isAnnotationDeclaredLocally(annotationType, clazz)) {
			return clazz;
		}
		return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
579 580 581
	}

	/**
582 583 584 585 586 587 588
	 * 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.
589
	 * <p>Meta-annotations will <em>not</em> be searched.
590 591 592 593 594 595 596 597
	 * <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
598
	 * {@code annotationTypes}, or {@code null} if not found
599
	 * @since 3.2.2
600 601 602 603 604
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
	 * @see #findAnnotationDeclaringClass(Class, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
	 */
605
	public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) {
606
		Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");
607
		if (clazz == null || Object.class == clazz) {
608 609 610 611 612 613 614 615
			return null;
		}
		for (Class<? extends Annotation> annotationType : annotationTypes) {
			if (isAnnotationDeclaredLocally(annotationType, clazz)) {
				return clazz;
			}
		}
		return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
A
Arjen Poutsma 已提交
616 617 618
	}

	/**
619 620 621 622
	 * 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.
623
	 * <p>Meta-annotations will <em>not</em> be searched.
624 625 626
	 * <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 已提交
627
	 * {@link #isAnnotationInherited(Class, Class)} instead.
628 629
	 * @param annotationType the annotation type to look for
	 * @param clazz the class to check for the annotation on
630
	 * @return {@code true} if an annotation of the specified {@code annotationType}
631
	 * is <em>directly present</em>
632 633
	 * @see java.lang.Class#getDeclaredAnnotations()
	 * @see java.lang.Class#getDeclaredAnnotation(Class)
A
Arjen Poutsma 已提交
634 635 636 637 638
	 * @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");
639
		try {
640 641
			for (Annotation ann : clazz.getDeclaredAnnotations()) {
				if (ann.annotationType().equals(annotationType)) {
642
					return true;
643
				}
A
Arjen Poutsma 已提交
644 645
			}
		}
646 647
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
648
			handleIntrospectionFailure(clazz, ex);
649
		}
650
		return false;
A
Arjen Poutsma 已提交
651 652 653
	}

	/**
654 655 656 657
	 * 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>).
658
	 * <p>Meta-annotations will <em>not</em> be searched.
659 660 661 662 663 664 665 666 667 668
	 * <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 已提交
669 670 671 672 673 674 675 676 677
	 * @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));
	}

678
	/**
J
Juergen Hoeller 已提交
679
	 * Determine if the supplied {@link Annotation} is defined in the core JDK
680
	 * {@code java.lang.annotation} package.
J
Juergen Hoeller 已提交
681
	 * @param annotation the annotation to check (never {@code null})
682 683 684 685
	 * @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");
686 687 688 689 690 691 692 693 694 695 696 697 698
		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");
699 700
	}

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

	/**
721 722 723
	 * 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 已提交
724 725
	 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
	 * However, the {@code Map} signature has been preserved for binary compatibility.
726
	 * @param annotation the annotation to retrieve the attributes for
727
	 * @param classValuesAsString whether to convert Class references into Strings (for
S
Sam Brannen 已提交
728
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
J
Juergen Hoeller 已提交
729
	 * or to preserve them as Class references
J
Juergen Hoeller 已提交
730
	 * @return the Map of annotation attributes, with attribute names as keys and
731 732
	 * corresponding attribute values as values; never {@code null}
	 * @see #getAnnotationAttributes(Annotation, boolean, boolean)
733
	 */
734
	public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
735 736 737 738
		return getAnnotationAttributes(annotation, classValuesAsString, false);
	}

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

759 760 761 762 763
	/**
	 * 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}.
764 765
	 * @param annotatedElement the element that is annotated with the supplied annotation;
	 * may be {@code null} if unknown
766
	 * @param annotation the annotation to retrieve the attributes for
767 768
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values; never {@code null}
769 770 771 772 773 774 775 776 777 778 779
	 * @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}.
780 781
	 * @param annotatedElement the element that is annotated with the supplied annotation;
	 * may be {@code null} if unknown
782 783 784 785
	 * @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
786
	 * @param nestedAnnotationsAsMap whether to convert nested annotations into
787 788
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
789
	 * {@code Annotation} instances
790 791 792 793 794 795 796
	 * @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) {

797
		return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap, false);
798 799 800 801 802 803 804 805
	}

	/**
	 * 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}.
	 *
806 807 808 809 810
	 * <p><strong>NOTE</strong>: This variant of {@code getAnnotationAttributes()} is
	 * only intended for use within the framework. Specifically, the {@code mergeMode} flag
	 * can be set to {@code true} in order to support processing of attribute aliases while
	 * merging attributes within an annotation hierarchy. When running in <em>merge mode</em>,
	 * the following special rules apply:
811
	 * <ol>
812 813 814 815
	 * <li>The supplied annotation will <em>not</em> be
	 * {@linkplain #synthesizeAnnotation synthesized} before retrieving its attributes;
	 * however, nested annotations and arrays of nested annotations <em>will</em> be
	 * synthesized.</li>
816
	 * <li>Default values will be replaced with {@link #DEFAULT_VALUE_PLACEHOLDER}.</li>
817 818 819 820 821 822 823 824
	 * <li>The resulting, merged annotation attributes should eventually be
	 * {@linkplain #postProcessAnnotationAttributes post-processed} in order to
	 * ensure that placeholders have been replaced by actual default values and
	 * in order to enforce {@code @AliasFor} semantics.</li>
	 * </ol>
	 *
	 * @param annotatedElement the element that is annotated with the supplied annotation;
	 * may be {@code null} if unknown
825 826 827 828
	 * @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
829
	 * @param nestedAnnotationsAsMap whether to convert nested annotations into
830 831
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
832
	 * {@code Annotation} instances
833 834
	 * @param mergeMode whether the annotation attributes should be created
	 * using <em>merge mode</em>
835 836 837
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values; never {@code null}
	 * @since 4.2
838
	 * @see #postProcessAnnotationAttributes
839
	 */
840
	static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation,
841
			boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean mergeMode) {
842

843
		if (!mergeMode) {
844
			annotation = synthesizeAnnotation(annotation, annotatedElement);
845 846 847 848 849 850 851 852
		}

		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();
853
				if (mergeMode && (defaultValue != null)) {
854 855 856
					if (ObjectUtils.nullSafeEquals(value, defaultValue)) {
						value = DEFAULT_VALUE_PLACEHOLDER;
					}
A
Arjen Poutsma 已提交
857
				}
858 859 860 861 862 863 864
				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 已提交
865
				}
866
				throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
A
Arjen Poutsma 已提交
867 868 869 870 871
			}
		}
		return attrs;
	}

872 873
	/**
	 * Adapt the given value according to the given class and nested annotation settings.
874
	 * <p>Nested annotations will be
875
	 * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}.
876 877
	 * @param annotatedElement the element that is annotated, used for contextual
	 * logging; may be {@code null} if unknown
878
	 * @param value the annotation attribute value
879
	 * @param classValuesAsString whether to convert Class references into Strings (for
S
Sam Brannen 已提交
880
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata})
881
	 * or to preserve them as Class references
882
	 * @param nestedAnnotationsAsMap whether to convert nested annotations into
883
	 * {@link AnnotationAttributes} maps (for compatibility with
S
Sam Brannen 已提交
884
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
885
	 * {@code Annotation} instances
886 887
	 * @return the adapted value, or the original value if no adaptation is needed
	 */
888 889 890
	static Object adaptValue(AnnotatedElement annotatedElement, Object value, boolean classValuesAsString,
			boolean nestedAnnotationsAsMap) {

891 892
		if (classValuesAsString) {
			if (value instanceof Class) {
893
				return ((Class<?>) value).getName();
894 895
			}
			else if (value instanceof Class[]) {
896
				Class<?>[] clazzArray = (Class<?>[]) value;
897
				String[] classNames = new String[clazzArray.length];
898
				for (int i = 0; i < clazzArray.length; i++) {
899
					classNames[i] = clazzArray[i].getName();
900
				}
901
				return classNames;
902 903
			}
		}
904 905 906 907 908 909 910 911 912

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

			if (nestedAnnotationsAsMap) {
				return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString,
					nestedAnnotationsAsMap);
			}
			else {
913
				return synthesizeAnnotation(annotation, annotatedElement);
914 915
			}
		}
916 917 918 919 920 921 922 923 924 925 926 927 928 929

		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 {
				for (int i = 0; i < annotations.length; i++) {
930
					annotations[i] = synthesizeAnnotation(annotations[i], annotatedElement);
931
				}
932
				return annotations;
933
			}
934
		}
935 936 937

		// Fallback
		return value;
938 939
	}

A
Arjen Poutsma 已提交
940
	/**
941
	 * Retrieve the <em>value</em> of the {@code value} attribute of a
J
Juergen Hoeller 已提交
942
	 * single-element Annotation, given an annotation instance.
A
Arjen Poutsma 已提交
943
	 * @param annotation the annotation instance from which to retrieve the value
944
	 * @return the attribute value, or {@code null} if not found
A
Arjen Poutsma 已提交
945 946 947 948 949 950 951
	 * @see #getValue(Annotation, String)
	 */
	public static Object getValue(Annotation annotation) {
		return getValue(annotation, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
952
	 * Retrieve the <em>value</em> of a named attribute, given an annotation instance.
A
Arjen Poutsma 已提交
953 954
	 * @param annotation the annotation instance from which to retrieve the value
	 * @param attributeName the name of the attribute value to retrieve
955
	 * @return the attribute value, or {@code null} if not found
J
Juergen Hoeller 已提交
956
	 * @see #getValue(Annotation)
A
Arjen Poutsma 已提交
957 958
	 */
	public static Object getValue(Annotation annotation, String attributeName) {
959
		if (annotation == null || !StringUtils.hasText(attributeName)) {
960 961
			return null;
		}
A
Arjen Poutsma 已提交
962
		try {
J
Juergen Hoeller 已提交
963
			Method method = annotation.annotationType().getDeclaredMethod(attributeName);
964
			ReflectionUtils.makeAccessible(method);
A
Arjen Poutsma 已提交
965 966 967 968 969 970 971 972
			return method.invoke(annotation);
		}
		catch (Exception ex) {
			return null;
		}
	}

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

	/**
J
Juergen Hoeller 已提交
984
	 * Retrieve the <em>default value</em> of a named attribute, given an annotation instance.
J
Juergen Hoeller 已提交
985
	 * @param annotation the annotation instance from which to retrieve the default value
A
Arjen Poutsma 已提交
986
	 * @param attributeName the name of the attribute value to retrieve
987
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
988 989 990
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Annotation annotation, String attributeName) {
991 992 993
		if (annotation == null) {
			return null;
		}
A
Arjen Poutsma 已提交
994 995 996 997
		return getDefaultValue(annotation.annotationType(), attributeName);
	}

	/**
998
	 * Retrieve the <em>default value</em> of the {@code value} attribute
J
Juergen Hoeller 已提交
999 1000
	 * 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
1001
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
1002 1003 1004 1005 1006 1007 1008
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
		return getDefaultValue(annotationType, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
1009 1010
	 * Retrieve the <em>default value</em> of a named attribute, given the
	 * {@link Class annotation type}.
J
Juergen Hoeller 已提交
1011
	 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
A
Arjen Poutsma 已提交
1012
	 * @param attributeName the name of the attribute value to retrieve.
1013
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
1014 1015 1016
	 * @see #getDefaultValue(Annotation, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
1017
		if (annotationType == null || !StringUtils.hasText(attributeName)) {
1018 1019
			return null;
		}
A
Arjen Poutsma 已提交
1020
		try {
J
Juergen Hoeller 已提交
1021
			return annotationType.getDeclaredMethod(attributeName).getDefaultValue();
A
Arjen Poutsma 已提交
1022 1023 1024 1025 1026 1027
		}
		catch (Exception ex) {
			return null;
		}
	}

1028
	/**
1029 1030 1031 1032
	 * <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}.
1033 1034
	 *
	 * @param annotation the annotation to synthesize
1035 1036 1037 1038 1039
	 * @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
1040
	 * @since 4.2
1041
	 * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1042
	 */
1043 1044
	static <A extends Annotation> A synthesizeAnnotation(A annotation) {
		return synthesizeAnnotation(annotation, null);
1045 1046 1047
	}

	/**
1048 1049 1050 1051
	 * <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}.
1052 1053
	 *
	 * @param annotation the annotation to synthesize
1054 1055
	 * @param annotatedElement the element that is annotated with the supplied
	 * annotation; may be {@code null} if unknown
1056
	 * @return the synthesized annotation if the supplied annotation is
1057
	 * <em>synthesizable</em>; {@code null} if the supplied annotation is
1058
	 * {@code null}; otherwise the supplied annotation unmodified
1059 1060
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
1061
	 * @since 4.2
1062
	 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
1063 1064
	 */
	@SuppressWarnings("unchecked")
1065
	public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) {
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
		if (annotation == null) {
			return null;
		}
		if (annotation instanceof SynthesizedAnnotation) {
			return annotation;
		}

		Class<? extends Annotation> annotationType = annotation.annotationType();
		if (!isSynthesizable(annotationType)) {
			return annotation;
		}

1078 1079 1080
		AnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation,
			annotatedElement);
		InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1081 1082 1083 1084 1085 1086
		A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
			(Class<A>) annotationType, SynthesizedAnnotation.class }, handler);

		return synthesizedAnnotation;
	}

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
	/**
	 * <em>Synthesize</em> the supplied map of annotation attributes by
	 * wrapping it in a dynamic proxy that implements an annotation of type
	 * {@code annotationType} and transparently enforces <em>attribute alias</em>
	 * semantics for annotation attributes that are annotated with
	 * {@link AliasFor @AliasFor}.
	 * <p>The supplied map must contain key-value pairs for every attribute
	 * defined by the supplied {@code annotationType}.
	 * <p>Note that {@link AnnotationAttributes} is a specialized type of
	 * {@link Map} that is a suitable candidate for this method's
	 * {@code attributes} argument.
	 *
	 * @param attributes the map of annotation attributes to synthesize
	 * @param annotationType the type of annotation to synthesize; never {@code null}
	 * @param annotatedElement the element that is annotated with the annotation
	 * corresponding to the supplied attributes; may be {@code null} if unknown
	 * @return the synthesized annotation, or {@code null} if the supplied attributes
	 * map is {@code null}
	 * @throws AnnotationConfigurationException if invalid configuration is detected
	 * @since 4.2
	 * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
	 */
	@SuppressWarnings("unchecked")
	public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes,
			Class<A> annotationType, AnnotatedElement annotatedElement) {
		Assert.notNull(annotationType, "annotationType must not be null");

		if (attributes == null) {
			return null;
		}

		AnnotationAttributeExtractor attributeExtractor = new MapAnnotationAttributeExtractor(attributes,
			annotationType, annotatedElement);
		InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
		A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
			annotationType, SynthesizedAnnotation.class }, handler);

		return synthesizedAnnotation;
	}
1126

1127
	/**
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
	 * 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}
1140 1141
	 * @since 4.2
	 */
1142
	static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
1143
		if (annotationType == null) {
1144 1145 1146
			return Collections.emptyMap();
		}

S
Sam Brannen 已提交
1147 1148 1149
		Map<String, String> map = attributeAliasesCache.get(annotationType);
		if (map != null) {
			return map;
1150 1151
		}

S
Sam Brannen 已提交
1152
		map = new HashMap<String, String>();
1153 1154 1155 1156 1157 1158 1159
		for (Method attribute : getAttributeMethods(annotationType)) {
			String attributeName = attribute.getName();
			String aliasedAttributeName = getAliasedAttributeName(attribute);
			if (aliasedAttributeName != null) {
				map.put(attributeName, aliasedAttributeName);
			}
		}
1160

S
Sam Brannen 已提交
1161
		attributeAliasesCache.put(annotationType, map);
1162

1163 1164 1165 1166
		return map;
	}

	/**
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
	 * 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>.
	 *
1177
	 * @since 4.2
1178 1179
	 * @see SynthesizedAnnotation
	 * @see SynthesizedAnnotationInvocationHandler
1180 1181 1182 1183
	 */
	@SuppressWarnings("unchecked")
	private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {

S
Sam Brannen 已提交
1184 1185 1186
		Boolean synthesizable = synthesizableCache.get(annotationType);
		if (synthesizable != null) {
			return synthesizable.booleanValue();
1187 1188
		}

S
Sam Brannen 已提交
1189
		synthesizable = Boolean.FALSE;
1190

1191 1192
		for (Method attribute : getAttributeMethods(annotationType)) {
			if (getAliasedAttributeName(attribute) != null) {
1193 1194
				synthesizable = Boolean.TRUE;
				break;
1195 1196 1197 1198 1199 1200 1201
			}

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

			if (Annotation[].class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType();
				if (isSynthesizable(nestedAnnotationType)) {
1202 1203
					synthesizable = Boolean.TRUE;
					break;
1204 1205 1206 1207 1208
				}
			}
			else if (Annotation.class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
				if (isSynthesizable(nestedAnnotationType)) {
1209 1210
					synthesizable = Boolean.TRUE;
					break;
1211 1212 1213 1214
				}
			}
		}

1215 1216 1217
		synthesizableCache.put(annotationType, synthesizable);

		return synthesizable.booleanValue();
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 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
	 * 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;
	}

	/**
1357 1358
	 * Get all methods declared in the supplied {@code annotationType} that
	 * match Java's requirements for annotation <em>attributes</em>.
1359
	 *
1360 1361 1362
	 * <p>All methods in the returned list will be
	 * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}.
	 *
S
Sam Brannen 已提交
1363 1364
	 * @param annotationType the type in which to search for attribute methods;
	 * never {@code null}
1365 1366
	 * @return all annotation attribute methods in the specified annotation
	 * type; never {@code null}, though potentially <em>empty</em>
1367 1368 1369
	 * @since 4.2
	 */
	static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
1370 1371 1372 1373 1374 1375 1376

		List<Method> methods = attributeMethodsCache.get(annotationType);
		if (methods != null) {
			return methods;
		}

		methods = new ArrayList<Method>();
1377
		for (Method method : annotationType.getDeclaredMethods()) {
1378
			if (isAttributeMethod(method)) {
1379
				ReflectionUtils.makeAccessible(method);
1380 1381 1382
				methods.add(method);
			}
		}
1383 1384 1385

		attributeMethodsCache.put(annotationType, methods);

1386 1387 1388
		return methods;
	}

1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406
	/**
	 * Determine if the supplied {@code method} is an annotation attribute method.
	 * @param method the method to check
	 * @return {@code true} if the method is an attribute method
	 */
	static boolean isAttributeMethod(Method method) {
		return ((method != null) && (method.getParameterTypes().length == 0) && (method.getReturnType() != void.class));
	}

	/**
	 * Determine if the supplied method is an "annotationType" method.
	 * @return {@code true} if the method is an "annotationType" method
	 * @see Annotation#annotationType()
	 */
	static boolean isAnnotationTypeMethod(Method method) {
		return ((method != null) && method.getName().equals("annotationType") && (method.getParameterTypes().length == 0));
	}

1407
	/**
1408
	 * Post-process the supplied {@link AnnotationAttributes}.
1409
	 *
1410 1411 1412 1413 1414 1415 1416 1417
	 * <p>Specifically, this method enforces <em>attribute alias</em> semantics
	 * for annotation attributes that are annotated with {@link AliasFor @AliasFor}
	 * and replaces {@linkplain #DEFAULT_VALUE_PLACEHOLDER placeholders} with their
	 * original default values.
	 *
	 * @param element the element that is annotated with an annotation or
	 * annotation hierarchy from which the supplied attributes were created;
	 * may be {@code null} if unknown
1418
	 * @param attributes the annotation attributes to post-process
1419 1420 1421 1422 1423 1424 1425
	 * @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 annotations into
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
	 * {@code Annotation} instances
1426
	 * @since 4.2
1427 1428 1429
	 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean, boolean)
	 * @see #DEFAULT_VALUE_PLACEHOLDER
	 * @see #getDefaultValue(Class, String)
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
	 */
	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
1442 1443 1444 1445 1446 1447 1448 1449 1450
		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);

1451 1452
				if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && (value != DEFAULT_VALUE_PLACEHOLDER)
						&& (aliasedValue != DEFAULT_VALUE_PLACEHOLDER)) {
1453 1454 1455 1456 1457 1458 1459 1460
					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);
				}
1461

1462
				// Replace default values with aliased values...
1463
				if (value == DEFAULT_VALUE_PLACEHOLDER) {
1464 1465 1466
					attributes.put(attributeName,
						adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
				}
1467
				if (aliasedValue == DEFAULT_VALUE_PLACEHOLDER) {
1468 1469
					attributes.put(aliasedAttributeName,
						adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
1470 1471 1472 1473 1474 1475
				}
			}
		}

		for (String attributeName : attributes.keySet()) {
			Object value = attributes.get(attributeName);
1476
			if (value == DEFAULT_VALUE_PLACEHOLDER) {
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
				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.
1489
	 * @param t the throwable to inspect
1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
	 * @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.
1506 1507
	 * @param element the element that we tried to introspect annotations on
	 * @param ex the exception that we encountered
1508
	 * @see #rethrowAnnotationConfigurationException
1509
	 */
1510 1511 1512 1513
	static void handleIntrospectionFailure(AnnotatedElement element, Exception ex) {

		rethrowAnnotationConfigurationException(ex);

1514 1515 1516 1517 1518
		Log loggerToUse = logger;
		if (loggerToUse == null) {
			loggerToUse = LogFactory.getLog(AnnotationUtils.class);
			logger = loggerToUse;
		}
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529
		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);
			}
1530 1531 1532 1533
		}
	}


1534
	/**
1535
	 * Cache key for the AnnotatedElement cache.
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567
	 */
	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());
		}
	}


1568 1569 1570 1571 1572 1573 1574 1575 1576 1577
	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 已提交
1578
		public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588
			this.containerAnnotationType = containerAnnotationType;
			this.annotationType = annotationType;
		}

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

		@SuppressWarnings("unchecked")
1589 1590 1591 1592
		private void process(AnnotatedElement element) {
			if (this.visited.add(element)) {
				try {
					for (Annotation ann : element.getAnnotations()) {
1593 1594
						Class<? extends Annotation> currentAnnotationType = ann.annotationType();
						if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
1595
							this.result.add(synthesizeAnnotation((A) ann, element));
1596
						}
1597
						else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
1598
							this.result.addAll(getValue(element, ann));
1599 1600
						}
						else if (!isInJavaLangAnnotationPackage(ann)) {
1601
							process(currentAnnotationType);
1602
						}
1603 1604
					}
				}
1605
				catch (Exception ex) {
1606
					handleIntrospectionFailure(element, ex);
1607
				}
1608 1609 1610 1611
			}
		}

		@SuppressWarnings("unchecked")
1612
		private List<A> getValue(AnnotatedElement element, Annotation annotation) {
1613 1614
			try {
				Method method = annotation.annotationType().getDeclaredMethod("value");
1615
				ReflectionUtils.makeAccessible(method);
1616 1617 1618 1619
				A[] annotations = (A[]) method.invoke(annotation);

				List<A> synthesizedAnnotations = new ArrayList<A>();
				for (A anno : annotations) {
1620
					synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
1621 1622
				}
				return synthesizedAnnotations;
1623 1624
			}
			catch (Exception ex) {
1625
				rethrowAnnotationConfigurationException(ex);
1626 1627
				// Unable to read value from repeating annotation container -> ignore it.
				return Collections.emptyList();
1628 1629 1630
			}
		}
	}
J
Juergen Hoeller 已提交
1631

A
Arjen Poutsma 已提交
1632
}