AnnotationUtils.java 68.9 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
import java.lang.reflect.Array;
22 23
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
A
Arjen Poutsma 已提交
24
import java.lang.reflect.Method;
25 26
import java.lang.reflect.Proxy;
import java.util.ArrayList;
27
import java.util.Collections;
28
import java.util.HashMap;
29 30
import java.util.HashSet;
import java.util.LinkedHashSet;
31
import java.util.List;
A
Arjen Poutsma 已提交
32
import java.util.Map;
33
import java.util.Set;
A
Arjen Poutsma 已提交
34

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

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

/**
47 48 49 50 51 52
 * 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 已提交
53
 *
S
Sam Brannen 已提交
54 55 56 57 58 59 60 61
 * <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 已提交
62
 *
63 64 65 66 67 68 69 70
 * <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.
 *
71
 * <h3>Meta-annotation Support</h3>
72 73 74 75 76
 * <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.
77
 *
78 79 80 81 82 83
 * <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 已提交
84 85 86 87
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Mark Fisher
C
Chris Beams 已提交
88
 * @author Chris Beams
89
 * @author Phillip Webb
A
Arjen Poutsma 已提交
90
 * @since 2.0
S
Sam Brannen 已提交
91 92 93 94
 * @see AliasFor
 * @see AnnotationAttributes
 * @see AnnotatedElementUtils
 * @see BridgeMethodResolver
95 96 97
 * @see java.lang.reflect.AnnotatedElement#getAnnotations()
 * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class)
 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations()
A
Arjen Poutsma 已提交
98 99 100
 */
public abstract class AnnotationUtils {

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

106

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

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

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

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

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

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

128 129
	private static transient Log logger;

J
Juergen Hoeller 已提交
130

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/**
	 * 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}.
	 *
807 808 809 810 811
	 * <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:
812
	 * <ol>
813 814 815 816
	 * <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>
817
	 * <li>Default values will be replaced with {@link #DEFAULT_VALUE_PLACEHOLDER}.</li>
818 819 820 821 822 823 824 825
	 * <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
826 827 828 829
	 * @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
830
	 * @param nestedAnnotationsAsMap whether to convert nested annotations into
831 832
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as
833
	 * {@code Annotation} instances
834 835
	 * @param mergeMode whether the annotation attributes should be created
	 * using <em>merge mode</em>
836 837 838
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values; never {@code null}
	 * @since 4.2
839
	 * @see #postProcessAnnotationAttributes
840
	 */
841
	static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation,
842
			boolean classValuesAsString, boolean nestedAnnotationsAsMap, boolean mergeMode) {
843

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

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

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

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

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

			if (nestedAnnotationsAsMap) {
				return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString,
					nestedAnnotationsAsMap);
			}
			else {
914
				return synthesizeAnnotation(annotation, annotatedElement);
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 {
930
				return synthesizeAnnotationArray(annotations, annotatedElement);
931
			}
932
		}
933 934 935

		// Fallback
		return value;
936 937
	}

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

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

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

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

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

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

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

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

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

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

		return synthesizedAnnotation;
	}

1085
	/**
1086 1087 1088 1089 1090
	 * <em>Synthesize</em> an annotation from the supplied map of annotation
	 * attributes by wrapping the map in a dynamic proxy that implements an
	 * annotation of the specified {@code annotationType} and transparently
	 * enforces <em>attribute alias</em> semantics for annotation attributes
	 * that are annotated with {@link AliasFor @AliasFor}.
1091 1092 1093
	 * <p>The supplied map must contain a key-value pair for every attribute
	 * defined in the supplied {@code annotationType} that is not aliased or
	 * does not have a default value.
1094
	 * <p>Note that {@link AnnotationAttributes} is a specialized type of
1095
	 * {@link Map} that is an ideal candidate for this method's
1096 1097 1098 1099 1100 1101 1102 1103
	 * {@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}
1104 1105 1106 1107
	 * @throws IllegalArgumentException if a required attribute is missing or if an
	 * attribute is not of the correct type
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
	 * @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;
	}
1128

1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
	/**
	 * <em>Synthesize</em> the supplied array of {@code annotations} by
	 * creating a new array of the same size and type and populating it
	 * with {@linkplain #synthesizeAnnotation(Annotation) synthesized}
	 * versions of the annotations from the input array.
	 *
	 * @param annotations the array of annotations to synthesize
	 * @param annotatedElement the element that is annotated with the supplied
	 * array of annotations; may be {@code null} if unknown
	 * @return a new array of synthesized annotations, or {@code null} if
	 * the supplied array is {@code null}
	 * @throws AnnotationConfigurationException if invalid configuration of
	 * {@code @AliasFor} is detected
	 * @since 4.2
	 * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
	 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement)
	 */
	public static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) {
		if (annotations == null) {
			return null;
		}

		Annotation[] synthesized = (Annotation[]) Array.newInstance(annotations.getClass().getComponentType(), annotations.length);
		for (int i = 0; i < annotations.length; i++) {
			synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement);
		}
		return synthesized;
	}

1158
	/**
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	 * 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}
1171 1172
	 * @since 4.2
	 */
1173
	static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
1174
		if (annotationType == null) {
1175 1176 1177
			return Collections.emptyMap();
		}

S
Sam Brannen 已提交
1178 1179 1180
		Map<String, String> map = attributeAliasesCache.get(annotationType);
		if (map != null) {
			return map;
1181 1182
		}

S
Sam Brannen 已提交
1183
		map = new HashMap<String, String>();
1184 1185 1186 1187 1188 1189 1190
		for (Method attribute : getAttributeMethods(annotationType)) {
			String attributeName = attribute.getName();
			String aliasedAttributeName = getAliasedAttributeName(attribute);
			if (aliasedAttributeName != null) {
				map.put(attributeName, aliasedAttributeName);
			}
		}
1191

S
Sam Brannen 已提交
1192
		attributeAliasesCache.put(annotationType, map);
1193

1194 1195 1196 1197
		return map;
	}

	/**
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
	 * 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>.
	 *
1208
	 * @since 4.2
1209 1210
	 * @see SynthesizedAnnotation
	 * @see SynthesizedAnnotationInvocationHandler
1211 1212 1213 1214
	 */
	@SuppressWarnings("unchecked")
	private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {

S
Sam Brannen 已提交
1215 1216 1217
		Boolean synthesizable = synthesizableCache.get(annotationType);
		if (synthesizable != null) {
			return synthesizable.booleanValue();
1218 1219
		}

S
Sam Brannen 已提交
1220
		synthesizable = Boolean.FALSE;
1221

1222 1223
		for (Method attribute : getAttributeMethods(annotationType)) {
			if (getAliasedAttributeName(attribute) != null) {
1224 1225
				synthesizable = Boolean.TRUE;
				break;
1226 1227 1228 1229 1230 1231 1232
			}

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

			if (Annotation[].class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType();
				if (isSynthesizable(nestedAnnotationType)) {
1233 1234
					synthesizable = Boolean.TRUE;
					break;
1235 1236 1237 1238 1239
				}
			}
			else if (Annotation.class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
				if (isSynthesizable(nestedAnnotationType)) {
1240 1241
					synthesizable = Boolean.TRUE;
					break;
1242 1243 1244 1245
				}
			}
		}

1246 1247 1248
		synthesizableCache.put(annotationType, synthesizable);

		return synthesizable.booleanValue();
1249
	}
1250

1251
	/**
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
	 * 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
	 * @since 4.2
1268
	 * @see #getAliasedAttributeName(Method, Class)
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 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387
	 */
	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;
	}

	/**
1388 1389
	 * Get all methods declared in the supplied {@code annotationType} that
	 * match Java's requirements for annotation <em>attributes</em>.
1390
	 *
1391 1392 1393
	 * <p>All methods in the returned list will be
	 * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}.
	 *
S
Sam Brannen 已提交
1394 1395
	 * @param annotationType the type in which to search for attribute methods;
	 * never {@code null}
1396 1397
	 * @return all annotation attribute methods in the specified annotation
	 * type; never {@code null}, though potentially <em>empty</em>
1398 1399 1400
	 * @since 4.2
	 */
	static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) {
1401 1402 1403 1404 1405 1406 1407

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

		methods = new ArrayList<Method>();
1408
		for (Method method : annotationType.getDeclaredMethods()) {
1409
			if (isAttributeMethod(method)) {
1410
				ReflectionUtils.makeAccessible(method);
1411 1412 1413
				methods.add(method);
			}
		}
1414 1415 1416

		attributeMethodsCache.put(annotationType, methods);

1417 1418 1419
		return methods;
	}

1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
	/**
	 * 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));
	}

1438
	/**
1439
	 * Post-process the supplied {@link AnnotationAttributes}.
1440
	 *
1441 1442 1443 1444 1445 1446 1447 1448
	 * <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
1449
	 * @param attributes the annotation attributes to post-process
1450 1451 1452 1453 1454 1455 1456
	 * @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
1457
	 * @since 4.2
1458 1459 1460
	 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean, boolean)
	 * @see #DEFAULT_VALUE_PLACEHOLDER
	 * @see #getDefaultValue(Class, String)
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472
	 */
	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
1473 1474 1475 1476 1477 1478 1479 1480 1481
		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);

1482 1483
				if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && (value != DEFAULT_VALUE_PLACEHOLDER)
						&& (aliasedValue != DEFAULT_VALUE_PLACEHOLDER)) {
1484 1485 1486 1487 1488 1489 1490 1491
					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);
				}
1492

1493
				// Replace default values with aliased values...
1494
				if (value == DEFAULT_VALUE_PLACEHOLDER) {
1495 1496 1497
					attributes.put(attributeName,
						adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
				}
1498
				if (aliasedValue == DEFAULT_VALUE_PLACEHOLDER) {
1499 1500
					attributes.put(aliasedAttributeName,
						adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
1501 1502 1503 1504 1505 1506
				}
			}
		}

		for (String attributeName : attributes.keySet()) {
			Object value = attributes.get(attributeName);
1507
			if (value == DEFAULT_VALUE_PLACEHOLDER) {
1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519
				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.
1520
	 * @param t the throwable to inspect
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536
	 * @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.
1537 1538
	 * @param element the element that we tried to introspect annotations on
	 * @param ex the exception that we encountered
1539
	 * @see #rethrowAnnotationConfigurationException
1540
	 */
1541 1542 1543 1544
	static void handleIntrospectionFailure(AnnotatedElement element, Exception ex) {

		rethrowAnnotationConfigurationException(ex);

1545 1546 1547 1548 1549
		Log loggerToUse = logger;
		if (loggerToUse == null) {
			loggerToUse = LogFactory.getLog(AnnotationUtils.class);
			logger = loggerToUse;
		}
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560
		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);
			}
1561 1562 1563 1564
		}
	}


1565
	/**
1566
	 * Cache key for the AnnotatedElement cache.
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598
	 */
	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());
		}
	}


1599 1600 1601 1602 1603 1604 1605 1606 1607 1608
	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 已提交
1609
		public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619
			this.containerAnnotationType = containerAnnotationType;
			this.annotationType = annotationType;
		}

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

		@SuppressWarnings("unchecked")
1620 1621 1622 1623
		private void process(AnnotatedElement element) {
			if (this.visited.add(element)) {
				try {
					for (Annotation ann : element.getAnnotations()) {
1624 1625
						Class<? extends Annotation> currentAnnotationType = ann.annotationType();
						if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
1626
							this.result.add(synthesizeAnnotation((A) ann, element));
1627
						}
1628
						else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
1629
							this.result.addAll(getValue(element, ann));
1630 1631
						}
						else if (!isInJavaLangAnnotationPackage(ann)) {
1632
							process(currentAnnotationType);
1633
						}
1634 1635
					}
				}
1636
				catch (Exception ex) {
1637
					handleIntrospectionFailure(element, ex);
1638
				}
1639 1640 1641 1642
			}
		}

		@SuppressWarnings("unchecked")
1643
		private List<A> getValue(AnnotatedElement element, Annotation annotation) {
1644 1645
			try {
				Method method = annotation.annotationType().getDeclaredMethod("value");
1646
				ReflectionUtils.makeAccessible(method);
1647 1648 1649 1650
				A[] annotations = (A[]) method.invoke(annotation);

				List<A> synthesizedAnnotations = new ArrayList<A>();
				for (A anno : annotations) {
1651
					synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
1652 1653
				}
				return synthesizedAnnotations;
1654 1655
			}
			catch (Exception ex) {
1656
				rethrowAnnotationConfigurationException(ex);
1657 1658
				// Unable to read value from repeating annotation container -> ignore it.
				return Collections.emptyList();
1659 1660 1661
			}
		}
	}
J
Juergen Hoeller 已提交
1662

A
Arjen Poutsma 已提交
1663
}