AnnotationUtils.java 68.3 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
		}
		catch (Exception ex) {
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
		}
		catch (Exception ex) {
184
			handleIntrospectionFailure(annotatedElement, ex);
185
		}
186
		return null;
187 188
	}

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

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

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

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

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

294 295 296 297
		try {
			if (annotatedElement.getAnnotations().length > 0) {
				return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement);
			}
298
		}
299
		catch (Exception ex) {
300
			handleIntrospectionFailure(annotatedElement, ex);
301 302
		}
		return Collections.emptySet();
303 304
	}

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

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

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

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

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

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

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

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

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

437
	static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
S
Sam Brannen 已提交
438 439 440
		Boolean found = annotatedInterfaceCache.get(iface);
		if (found != null) {
			return found.booleanValue();
441
		}
S
Sam Brannen 已提交
442
		found = Boolean.FALSE;
443 444 445
		for (Method ifcMethod : iface.getMethods()) {
			try {
				if (ifcMethod.getAnnotations().length > 0) {
446
					found = Boolean.TRUE;
447
					break;
448
				}
449 450
			}
			catch (Exception ex) {
451
				handleIntrospectionFailure(ifcMethod, ex);
452 453
			}
		}
454
		annotatedInterfaceCache.put(iface, found);
455
		return found.booleanValue();
456 457
	}

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

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

		try {
			Annotation[] anns = clazz.getDeclaredAnnotations();
			for (Annotation ann : anns) {
				if (ann.annotationType().equals(annotationType)) {
					return (A) ann;
				}
512
			}
513 514 515 516 517 518
			for (Annotation ann : anns) {
				if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
					A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
					if (annotation != null) {
						return annotation;
					}
519
				}
520
			}
A
Arjen Poutsma 已提交
521
		}
522
		catch (Exception ex) {
523
			handleIntrospectionFailure(clazz, ex);
524 525 526
			return null;
		}

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

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

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

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

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

	/**
646 647 648 649
	 * 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>).
650
	 * <p>Meta-annotations will <em>not</em> be searched.
651 652 653 654 655 656 657 658 659 660
	 * <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 已提交
661 662 663 664 665 666 667 668 669
	 * @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));
	}

670
	/**
J
Juergen Hoeller 已提交
671
	 * Determine if the supplied {@link Annotation} is defined in the core JDK
672
	 * {@code java.lang.annotation} package.
J
Juergen Hoeller 已提交
673
	 * @param annotation the annotation to check (never {@code null})
674 675 676 677
	 * @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");
678 679 680 681 682 683 684 685 686 687 688 689 690
		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");
691 692
	}

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

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

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

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

789
		return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap, false);
790 791 792 793 794 795 796 797
	}

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

835
		if (!mergeMode) {
836
			annotation = synthesizeAnnotation(annotation, annotatedElement);
837 838 839 840 841 842 843 844
		}

		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();
845
				if (mergeMode && (defaultValue != null)) {
846 847 848
					if (ObjectUtils.nullSafeEquals(value, defaultValue)) {
						value = DEFAULT_VALUE_PLACEHOLDER;
					}
A
Arjen Poutsma 已提交
849
				}
850 851 852 853 854 855 856
				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 已提交
857
				}
858
				throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex);
A
Arjen Poutsma 已提交
859 860 861 862 863
			}
		}
		return attrs;
	}

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

883 884
		if (classValuesAsString) {
			if (value instanceof Class) {
885
				return ((Class<?>) value).getName();
886 887
			}
			else if (value instanceof Class[]) {
888
				Class<?>[] clazzArray = (Class<?>[]) value;
889
				String[] classNames = new String[clazzArray.length];
890
				for (int i = 0; i < clazzArray.length; i++) {
891
					classNames[i] = clazzArray[i].getName();
892
				}
893
				return classNames;
894 895
			}
		}
896 897 898 899 900 901 902 903 904

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

			if (nestedAnnotationsAsMap) {
				return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString,
					nestedAnnotationsAsMap);
			}
			else {
905
				return synthesizeAnnotation(annotation, annotatedElement);
906 907
			}
		}
908 909 910 911 912 913 914 915 916 917 918 919 920

		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 {
921
				return synthesizeAnnotationArray(annotations, annotatedElement);
922
			}
923
		}
924 925 926

		// Fallback
		return value;
927 928
	}

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

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

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

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

	/**
987
	 * Retrieve the <em>default value</em> of the {@code value} attribute
J
Juergen Hoeller 已提交
988 989
	 * 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
990
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
991 992 993 994 995 996 997
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
		return getDefaultValue(annotationType, VALUE);
	}

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

1017
	/**
1018 1019 1020 1021
	 * <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}.
1022 1023
	 *
	 * @param annotation the annotation to synthesize
1024 1025 1026 1027 1028
	 * @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
1029
	 * @since 4.2
1030
	 * @see #synthesizeAnnotation(Annotation, AnnotatedElement)
1031
	 */
1032 1033
	static <A extends Annotation> A synthesizeAnnotation(A annotation) {
		return synthesizeAnnotation(annotation, null);
1034 1035 1036
	}

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

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

1067 1068 1069
		AnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation,
			annotatedElement);
		InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1070 1071 1072 1073 1074 1075
		A synthesizedAnnotation = (A) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] {
			(Class<A>) annotationType, SynthesizedAnnotation.class }, handler);

		return synthesizedAnnotation;
	}

1076
	/**
1077 1078 1079 1080 1081
	 * <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}.
1082 1083 1084
	 * <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.
1085
	 * <p>Note that {@link AnnotationAttributes} is a specialized type of
1086
	 * {@link Map} that is an ideal candidate for this method's
1087 1088 1089 1090 1091 1092 1093 1094
	 * {@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}
1095 1096 1097 1098
	 * @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
1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
	 * @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;
	}
1119

1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
	/**
	 * <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;
	}

1149
	/**
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
	 * 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}
1162 1163
	 * @since 4.2
	 */
1164
	static Map<String, String> getAttributeAliasMap(Class<? extends Annotation> annotationType) {
1165
		if (annotationType == null) {
1166 1167 1168
			return Collections.emptyMap();
		}

S
Sam Brannen 已提交
1169 1170 1171
		Map<String, String> map = attributeAliasesCache.get(annotationType);
		if (map != null) {
			return map;
1172 1173
		}

S
Sam Brannen 已提交
1174
		map = new HashMap<String, String>();
1175 1176 1177 1178 1179 1180 1181
		for (Method attribute : getAttributeMethods(annotationType)) {
			String attributeName = attribute.getName();
			String aliasedAttributeName = getAliasedAttributeName(attribute);
			if (aliasedAttributeName != null) {
				map.put(attributeName, aliasedAttributeName);
			}
		}
1182

S
Sam Brannen 已提交
1183
		attributeAliasesCache.put(annotationType, map);
1184

1185 1186 1187 1188
		return map;
	}

	/**
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
	 * 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>.
	 *
1199
	 * @since 4.2
1200 1201
	 * @see SynthesizedAnnotation
	 * @see SynthesizedAnnotationInvocationHandler
1202 1203 1204 1205
	 */
	@SuppressWarnings("unchecked")
	private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {

S
Sam Brannen 已提交
1206 1207 1208
		Boolean synthesizable = synthesizableCache.get(annotationType);
		if (synthesizable != null) {
			return synthesizable.booleanValue();
1209 1210
		}

S
Sam Brannen 已提交
1211
		synthesizable = Boolean.FALSE;
1212

1213 1214
		for (Method attribute : getAttributeMethods(annotationType)) {
			if (getAliasedAttributeName(attribute) != null) {
1215 1216
				synthesizable = Boolean.TRUE;
				break;
1217 1218 1219 1220 1221 1222 1223
			}

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

			if (Annotation[].class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType();
				if (isSynthesizable(nestedAnnotationType)) {
1224 1225
					synthesizable = Boolean.TRUE;
					break;
1226 1227 1228 1229 1230
				}
			}
			else if (Annotation.class.isAssignableFrom(returnType)) {
				Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
				if (isSynthesizable(nestedAnnotationType)) {
1231 1232
					synthesizable = Boolean.TRUE;
					break;
1233 1234 1235 1236
				}
			}
		}

1237 1238 1239
		synthesizableCache.put(annotationType, synthesizable);

		return synthesizable.booleanValue();
1240
	}
1241

1242
	/**
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
	 * 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
1259
	 * @see #getAliasedAttributeName(Method, Class)
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 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
	 */
	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;
	}

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

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

		methods = new ArrayList<Method>();
1399
		for (Method method : annotationType.getDeclaredMethods()) {
1400
			if (isAttributeMethod(method)) {
1401
				ReflectionUtils.makeAccessible(method);
1402 1403 1404
				methods.add(method);
			}
		}
1405 1406 1407

		attributeMethodsCache.put(annotationType, methods);

1408 1409 1410
		return methods;
	}

1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
	/**
	 * 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));
	}

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

1473 1474
				if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && (value != DEFAULT_VALUE_PLACEHOLDER)
						&& (aliasedValue != DEFAULT_VALUE_PLACEHOLDER)) {
1475 1476 1477 1478 1479 1480 1481 1482
					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);
				}
1483

1484
				// Replace default values with aliased values...
1485
				if (value == DEFAULT_VALUE_PLACEHOLDER) {
1486 1487 1488
					attributes.put(attributeName,
						adaptValue(element, aliasedValue, classValuesAsString, nestedAnnotationsAsMap));
				}
1489
				if (aliasedValue == DEFAULT_VALUE_PLACEHOLDER) {
1490 1491
					attributes.put(aliasedAttributeName,
						adaptValue(element, value, classValuesAsString, nestedAnnotationsAsMap));
1492 1493 1494 1495 1496 1497
				}
			}
		}

		for (String attributeName : attributes.keySet()) {
			Object value = attributes.get(attributeName);
1498
			if (value == DEFAULT_VALUE_PLACEHOLDER) {
1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510
				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.
1511
	 * @param t the throwable to inspect
1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
	 * @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
1526 1527 1528 1529 1530
	 * {@code TypeNotPresentExceptions}) before moving on, assuming nested
	 * Class values were not resolvable within annotation attributes and
	 * thereby effectively pretending there were no annotations on the specified
	 * element.
	 *
1531 1532
	 * @param element the element that we tried to introspect annotations on
	 * @param ex the exception that we encountered
1533
	 * @see #rethrowAnnotationConfigurationException
1534
	 */
1535 1536 1537 1538
	static void handleIntrospectionFailure(AnnotatedElement element, Exception ex) {

		rethrowAnnotationConfigurationException(ex);

1539 1540 1541 1542 1543
		Log loggerToUse = logger;
		if (loggerToUse == null) {
			loggerToUse = LogFactory.getLog(AnnotationUtils.class);
			logger = loggerToUse;
		}
1544
		if ((element instanceof Class) && Annotation.class.isAssignableFrom((Class<?>) element)) {
1545
			// Meta-annotation lookup on an annotation type
1546 1547
			if (loggerToUse.isDebugEnabled()) {
				loggerToUse.debug("Failed to introspect meta-annotations on [" + element + "]: " + ex);
1548 1549 1550 1551 1552
			}
		}
		else {
			// Direct annotation lookup on regular Class, Method, Field
			if (loggerToUse.isInfoEnabled()) {
1553
				loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex);
1554
			}
1555 1556 1557 1558
		}
	}


1559
	/**
1560
	 * Cache key for the AnnotatedElement cache.
1561 1562 1563 1564 1565 1566 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
	 */
	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());
		}
	}


1593 1594 1595 1596 1597 1598 1599 1600 1601 1602
	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 已提交
1603
		public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
1604 1605 1606 1607 1608 1609 1610 1611 1612 1613
			this.containerAnnotationType = containerAnnotationType;
			this.annotationType = annotationType;
		}

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

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

		@SuppressWarnings("unchecked")
1637
		private List<A> getValue(AnnotatedElement element, Annotation annotation) {
1638 1639
			try {
				Method method = annotation.annotationType().getDeclaredMethod("value");
1640
				ReflectionUtils.makeAccessible(method);
1641 1642 1643 1644
				A[] annotations = (A[]) method.invoke(annotation);

				List<A> synthesizedAnnotations = new ArrayList<A>();
				for (A anno : annotations) {
1645
					synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
1646 1647
				}
				return synthesizedAnnotations;
1648 1649
			}
			catch (Exception ex) {
1650
				handleIntrospectionFailure(element, ex);
1651
			}
1652 1653
			// Unable to read value from repeating annotation container -> ignore it.
			return Collections.emptyList();
1654 1655
		}
	}
J
Juergen Hoeller 已提交
1656

A
Arjen Poutsma 已提交
1657
}