AnnotationUtils.java 32.9 KB
Newer Older
A
Arjen Poutsma 已提交
1
/*
2
 * Copyright 2002-2014 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;
A
Arjen Poutsma 已提交
21
import java.lang.reflect.Method;
22 23 24 25
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
26
import java.util.List;
A
Arjen Poutsma 已提交
27
import java.util.Map;
28
import java.util.Set;
A
Arjen Poutsma 已提交
29

30 31 32
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

A
Arjen Poutsma 已提交
33 34
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
35
import org.springframework.util.ConcurrentReferenceHashMap;
36
import org.springframework.util.ObjectUtils;
37
import org.springframework.util.ReflectionUtils;
A
Arjen Poutsma 已提交
38 39

/**
S
Sam Brannen 已提交
40 41 42 43
 * General utility methods for working with annotations, handling bridge methods
 * (which the compiler generates for generic declarations) as well as super methods
 * (for optional "annotation inheritance"). Note that none of this is
 * provided by the JDK's introspection facilities themselves.
A
Arjen Poutsma 已提交
44
 *
S
Sam Brannen 已提交
45 46 47 48 49 50 51 52
 * <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 已提交
53 54 55 56 57
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Mark Fisher
C
Chris Beams 已提交
58
 * @author Chris Beams
59
 * @author Phillip Webb
A
Arjen Poutsma 已提交
60 61 62 63 64 65 66
 * @since 2.0
 * @see java.lang.reflect.Method#getAnnotations()
 * @see java.lang.reflect.Method#getAnnotation(Class)
 */
public abstract class AnnotationUtils {

	/** The attribute name for annotations with a single element */
67
	public static final String VALUE = "value";
A
Arjen Poutsma 已提交
68

69 70
	private static final Log logger = LogFactory.getLog(AnnotationUtils.class);

71 72 73 74 75
	private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache =
			new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256);

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

J
Juergen Hoeller 已提交
77

78 79 80 81
	/**
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
	 * annotation: either the given annotation itself or a meta-annotation thereof.
	 * @param ann the Annotation to check
J
Juergen Hoeller 已提交
82 83
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
	 * @return the matching annotation, or {@code null} if none found
84 85 86 87 88 89 90
	 * @since 4.0
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> annotationType) {
		if (annotationType.isInstance(ann)) {
			return (T) ann;
		}
91 92 93 94 95 96
		try {
			return ann.annotationType().getAnnotation(annotationType);
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
			// We're probably hitting a non-present optional arrangement - let's back out.
97 98 99
			if (logger.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + ann.annotationType() + "]: " + ex);
			}
100 101
			return null;
		}
102 103
	}

104
	/**
C
Chris Beams 已提交
105 106 107
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied
	 * Method, Constructor or Field. Meta-annotations will be searched if the annotation
	 * is not declared locally on the supplied element.
108
	 * @param annotatedElement the Method, Constructor or Field from which to get the annotation
J
Juergen Hoeller 已提交
109 110
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
	 * @return the matching annotation, or {@code null} if none found
C
Chris Beams 已提交
111
	 * @since 3.1
112
	 */
113
	public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedElement, Class<T> annotationType) {
114
		try {
115
			T ann = annotatedElement.getAnnotation(annotationType);
116
			if (ann == null) {
117
				for (Annotation metaAnn : annotatedElement.getAnnotations()) {
118 119 120 121
					ann = metaAnn.annotationType().getAnnotation(annotationType);
					if (ann != null) {
						break;
					}
122 123
				}
			}
124 125 126 127 128
			return ann;
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
			// We're probably hitting a non-present optional arrangement - let's back out.
129 130 131
			if (logger.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
			}
132
			return null;
133 134 135
		}
	}

A
Arjen Poutsma 已提交
136 137 138 139 140 141 142 143
	/**
	 * Get all {@link Annotation Annotations} from the supplied {@link Method}.
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
	 * @param method the method to look for annotations on
	 * @return the annotations found
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
	 */
	public static Annotation[] getAnnotations(Method method) {
144 145 146 147 148 149
		try {
			return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
		}
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
			// We're probably hitting a non-present optional arrangement - let's back out.
150 151 152
			if (logger.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + method + "]: " + ex);
			}
153 154
			return null;
		}
A
Arjen Poutsma 已提交
155 156 157
	}

	/**
158
	 * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}.
A
Arjen Poutsma 已提交
159 160
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
	 * @param method the method to look for annotations on
J
Juergen Hoeller 已提交
161
	 * @param annotationType the annotation type to look for
A
Arjen Poutsma 已提交
162 163 164 165
	 * @return the annotations found
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
	 */
	public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
166
		Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
167
		return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
A
Arjen Poutsma 已提交
168 169
	}

170 171 172 173 174 175 176
	/**
	 * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
	 * supplied {@link Method}. Deals with both a single direct annotation and repeating
	 * annotations nested within a containing annotation.
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
	 * @param method the method to look for annotations on
	 * @param containerAnnotationType the class of the container that holds the annotations
J
Juergen Hoeller 已提交
177
	 * @param annotationType the annotation type to look for
178 179
	 * @return the annotations found
	 * @since 4.0
J
Juergen Hoeller 已提交
180
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
181 182 183
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
184

185
		Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
J
Juergen Hoeller 已提交
186
		return getRepeatableAnnotation((AnnotatedElement) resolvedMethod, containerAnnotationType, annotationType);
187 188 189 190 191 192 193 194 195
	}

	/**
	 * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
	 * supplied {@link AnnotatedElement}. Deals with both a single direct annotation and
	 * repeating annotations nested within a containing annotation.
	 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
	 * @param annotatedElement the element to look for annotations on
	 * @param containerAnnotationType the class of the container that holds the annotations
J
Juergen Hoeller 已提交
196
	 * @param annotationType the annotation type to look for
197 198
	 * @return the annotations found
	 * @since 4.0
J
Juergen Hoeller 已提交
199
	 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
200 201 202
	 */
	public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
			Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
J
Juergen Hoeller 已提交
203

204 205 206 207
		try {
			if (annotatedElement.getAnnotations().length > 0) {
				return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement);
			}
208
		}
209 210 211
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
			// We're probably hitting a non-present optional arrangement - let's back out.
212 213 214
			if (logger.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
			}
215 216
		}
		return Collections.emptySet();
217 218
	}

A
Arjen Poutsma 已提交
219
	/**
S
Sam Brannen 已提交
220
	 * Find a single {@link Annotation} of {@code annotationType} from the supplied
J
Juergen Hoeller 已提交
221
	 * {@link Method}, traversing its super methods (i.e., from superclasses and
S
Sam Brannen 已提交
222 223 224
	 * interfaces) if no annotation can be found on the given method itself.
	 * <p>Annotations on methods are not inherited by default, so we need to handle
	 * this explicitly.
A
Arjen Poutsma 已提交
225
	 * @param method the method to look for annotations on
J
Juergen Hoeller 已提交
226 227
	 * @param annotationType the annotation type to look for
	 * @return the annotation found, or {@code null} if none
A
Arjen Poutsma 已提交
228
	 */
229
	@SuppressWarnings("unchecked")
A
Arjen Poutsma 已提交
230
	public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
231 232 233 234 235 236 237
		AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType);
		A result = (A) findAnnotationCache.get(cacheKey);
		if (result == null) {
			result = getAnnotation(method, annotationType);
			Class<?> clazz = method.getDeclaringClass();
			if (result == null) {
				result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
A
Arjen Poutsma 已提交
238
			}
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
			while (result == null) {
				clazz = clazz.getSuperclass();
				if (clazz == null || clazz.equals(Object.class)) {
					break;
				}
				try {
					Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
					result = getAnnotation(equivalentMethod, annotationType);
				}
				catch (NoSuchMethodException ex) {
					// No equivalent method found
				}
				if (result == null) {
					result = searchOnInterfaces(method, annotationType, clazz.getInterfaces());
				}
254
			}
255 256
			if (result != null) {
				findAnnotationCache.put(cacheKey, result);
A
Arjen Poutsma 已提交
257 258
			}
		}
259
		return result;
A
Arjen Poutsma 已提交
260 261
	}

262
	private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
263
		A annotation = null;
J
Juergen Hoeller 已提交
264
		for (Class<?> iface : ifcs) {
265 266 267 268 269 270 271 272 273 274 275
			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;
				}
276
			}
277 278 279 280 281
		}
		return annotation;
	}

	private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
282 283 284 285 286 287 288 289 290 291
		Boolean flag = annotatedInterfaceCache.get(iface);
		if (flag != null) {
			return flag;
		}
		boolean found = false;
		for (Method ifcMethod : iface.getMethods()) {
			try {
				if (ifcMethod.getAnnotations().length > 0) {
					found = true;
					break;
292
				}
293 294 295 296 297 298
			}
			catch (Exception ex) {
				// Assuming nested Class values not resolvable within annotation attributes...
				// We're probably hitting a non-present optional arrangement - let's back out.
				if (logger.isInfoEnabled()) {
					logger.info("Failed to introspect annotations on [" + ifcMethod + "]: " + ex);
299
				}
300 301
			}
		}
302 303
		annotatedInterfaceCache.put(iface, found);
		return found;
304 305
	}

A
Arjen Poutsma 已提交
306
	/**
307 308 309 310
	 * Find a single {@link Annotation} of {@code annotationType} on the
	 * supplied {@link Class}, traversing its interfaces, annotations, and
	 * superclasses if the annotation is not <em>present</em> on the given class
	 * itself.
S
Sam Brannen 已提交
311
	 * <p>This method explicitly handles class-level annotations which are not
312 313
	 * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
	 * as meta-annotations and annotations on interfaces</em>.
S
Sam Brannen 已提交
314 315
	 * <p>The algorithm operates as follows:
	 * <ol>
316 317 318 319
	 * <li>Search for the annotation on the given class and return it if found.
	 * <li>Recursively search through all interfaces that the given class declares.
	 * <li>Recursively search through all annotations that the given class declares.
	 * <li>Recursively search through the superclass hierarchy of the given class.
S
Sam Brannen 已提交
320
	 * </ol>
321 322 323
	 * <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 已提交
324
	 * @param clazz the class to look for annotations on
325 326
	 * @param annotationType the type of annotation to look for
	 * @return the annotation if found, or {@code null} if not found
A
Arjen Poutsma 已提交
327
	 */
328
	@SuppressWarnings("unchecked")
A
Arjen Poutsma 已提交
329
	public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
330 331 332 333 334 335 336 337 338
		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);
			}
		}
		return result;
339 340 341 342 343
	}

	/**
	 * Perform the search algorithm for {@link #findAnnotation(Class, Class)},
	 * avoiding endless recursion by tracking which annotations have already
S
Sam Brannen 已提交
344
	 * been <em>visited</em>.
345 346
	 * @param clazz the class to look for annotations on
	 * @param annotationType the type of annotation to look for
S
Sam Brannen 已提交
347
	 * @param visited the set of annotations that have already been visited
348 349
	 * @return the annotation if found, or {@code null} if not found
	 */
J
Juergen Hoeller 已提交
350
	private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
A
Arjen Poutsma 已提交
351
		Assert.notNull(clazz, "Class must not be null");
352
		if (isAnnotationDeclaredLocally(annotationType, clazz)) {
353 354 355 356 357 358
			try {
				return clazz.getAnnotation(annotationType);
			}
			catch (Exception ex) {
				// Assuming nested Class values not resolvable within annotation attributes...
				// We're probably hitting a non-present optional arrangement - let's back out.
359 360 361
				if (logger.isInfoEnabled()) {
					logger.info("Failed to introspect annotations on [" + clazz + "]: " + ex);
				}
362 363
				return null;
			}
A
Arjen Poutsma 已提交
364 365
		}
		for (Class<?> ifc : clazz.getInterfaces()) {
366
			A annotation = findAnnotation(ifc, annotationType, visited);
A
Arjen Poutsma 已提交
367 368 369 370
			if (annotation != null) {
				return annotation;
			}
		}
371
		for (Annotation ann : clazz.getDeclaredAnnotations()) {
372
			if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
J
Juergen Hoeller 已提交
373
				A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
374 375 376 377 378
				if (annotation != null) {
					return annotation;
				}
			}
		}
379 380
		Class<?> superclass = clazz.getSuperclass();
		if (superclass == null || superclass.equals(Object.class)) {
A
Arjen Poutsma 已提交
381 382
			return null;
		}
S
Sam Brannen 已提交
383
		return findAnnotation(superclass, annotationType, visited);
A
Arjen Poutsma 已提交
384 385 386
	}

	/**
387 388 389 390 391
	 * Find the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}
	 * (including the specified {@code clazz} itself) which declares an annotation for the
	 * specified {@code annotationType}, or {@code null} if not found. If the supplied
	 * {@code clazz} is {@code null}, {@code null} will be returned.
	 * <p>If the supplied {@code clazz} is an interface, only the interface itself will be checked;
J
Juergen Hoeller 已提交
392 393 394 395
	 * the inheritance hierarchy for interfaces will not be traversed.
	 * <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.
J
Juergen Hoeller 已提交
396 397
	 * @param annotationType the annotation type to look for, both locally and as a meta-annotation
	 * @param clazz the class on which to check for the annotation (may be {@code null})
398 399
	 * @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}
	 * which declares an annotation for the specified {@code annotationType}, or {@code null}
J
Juergen Hoeller 已提交
400
	 * if not found
A
Arjen Poutsma 已提交
401 402
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
403 404
	 * @see #findAnnotationDeclaringClassForTypes(List, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
A
Arjen Poutsma 已提交
405 406 407 408 409 410
	 */
	public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
		Assert.notNull(annotationType, "Annotation type must not be null");
		if (clazz == null || clazz.equals(Object.class)) {
			return null;
		}
411 412 413 414
		if (isAnnotationDeclaredLocally(annotationType, clazz)) {
			return clazz;
		}
		return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	}

	/**
	 * Find the first {@link Class} in the inheritance hierarchy of the specified
	 * {@code clazz} (including the specified {@code clazz} itself) which declares
	 * at least one of the specified {@code annotationTypes}, or {@code null} if
	 * none of the specified annotation types could be found.
	 * <p>If the supplied {@code clazz} is {@code null}, {@code null} will be
	 * returned.
	 * <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.
	 * <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 list of Class objects corresponding to the
	 * annotation types
	 * @param clazz the Class object corresponding to the class on which to check
	 * for the annotations, or {@code null}
	 * @return the first {@link Class} in the inheritance hierarchy of the specified
	 * {@code clazz} which declares an annotation of at least one of the specified
	 * {@code annotationTypes}, or {@code null} if not found
437
	 * @since 3.2.2
438 439 440 441 442
	 * @see Class#isAnnotationPresent(Class)
	 * @see Class#getDeclaredAnnotations()
	 * @see #findAnnotationDeclaringClass(Class, Class)
	 * @see #isAnnotationDeclaredLocally(Class, Class)
	 */
443
	public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) {
444 445 446 447 448 449 450 451 452 453
		Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");
		if (clazz == null || clazz.equals(Object.class)) {
			return null;
		}
		for (Class<? extends Annotation> annotationType : annotationTypes) {
			if (isAnnotationDeclaredLocally(annotationType, clazz)) {
				return clazz;
			}
		}
		return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());
A
Arjen Poutsma 已提交
454 455 456
	}

	/**
457 458
	 * Determine whether an annotation for the specified {@code annotationType} is
	 * declared locally on the supplied {@code clazz}. The supplied {@link Class}
J
Juergen Hoeller 已提交
459 460
	 * may represent any type.
	 * <p>Note: This method does <strong>not</strong> determine if the annotation is
S
Sam Brannen 已提交
461 462 463
	 * {@linkplain java.lang.annotation.Inherited inherited}. For greater clarity
	 * regarding inherited annotations, consider using
	 * {@link #isAnnotationInherited(Class, Class)} instead.
A
Arjen Poutsma 已提交
464
	 * @param annotationType the Class object corresponding to the annotation type
J
Juergen Hoeller 已提交
465
	 * @param clazz the Class object corresponding to the class on which to check for the annotation
466 467
	 * @return {@code true} if an annotation for the specified {@code annotationType}
	 * is declared locally on the supplied {@code clazz}
A
Arjen Poutsma 已提交
468 469 470 471 472 473 474
	 * @see Class#getDeclaredAnnotations()
	 * @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");
		boolean declaredLocally = false;
475 476 477 478 479 480
		try {
			for (Annotation annotation : clazz.getDeclaredAnnotations()) {
				if (annotation.annotationType().equals(annotationType)) {
					declaredLocally = true;
					break;
				}
A
Arjen Poutsma 已提交
481 482
			}
		}
483 484 485
		catch (Exception ex) {
			// Assuming nested Class values not resolvable within annotation attributes...
			// We're probably hitting a non-present optional arrangement - let's back out.
486 487 488
			if (logger.isInfoEnabled()) {
				logger.info("Failed to introspect annotations on [" + clazz + "]: " + ex);
			}
489
		}
A
Arjen Poutsma 已提交
490 491 492 493
		return declaredLocally;
	}

	/**
494
	 * Determine whether an annotation for the specified {@code annotationType} is present
S
Sam Brannen 已提交
495 496
	 * on the supplied {@code clazz} and is {@linkplain java.lang.annotation.Inherited inherited}
	 * (i.e., not declared locally for the class).
497
	 * <p>If the supplied {@code clazz} is an interface, only the interface itself will be checked.
J
Juergen Hoeller 已提交
498
	 * In accordance with standard meta-annotation semantics, the inheritance hierarchy for interfaces
S
Sam Brannen 已提交
499 500
	 * will not be traversed. See the {@linkplain java.lang.annotation.Inherited Javadoc} for the
	 * {@code @Inherited} meta-annotation for further details regarding annotation inheritance.
A
Arjen Poutsma 已提交
501
	 * @param annotationType the Class object corresponding to the annotation type
J
Juergen Hoeller 已提交
502
	 * @param clazz the Class object corresponding to the class on which to check for the annotation
503
	 * @return {@code true} if an annotation for the specified {@code annotationType} is present
S
Sam Brannen 已提交
504
	 * on the supplied {@code clazz} and is <em>inherited</em>
A
Arjen Poutsma 已提交
505 506 507 508 509 510 511 512 513
	 * @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));
	}

514
	/**
J
Juergen Hoeller 已提交
515
	 * Determine if the supplied {@link Annotation} is defined in the core JDK
516
	 * {@code java.lang.annotation} package.
J
Juergen Hoeller 已提交
517
	 * @param annotation the annotation to check (never {@code null})
518 519 520 521 522 523 524
	 * @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");
		return annotation.annotationType().getName().startsWith("java.lang.annotation");
	}

A
Arjen Poutsma 已提交
525
	/**
J
Juergen Hoeller 已提交
526 527 528 529
	 * Retrieve the given annotation's attributes as a {@link Map}, preserving all
	 * attribute types as-is.
	 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
	 * However, the {@code Map} signature has been preserved for binary compatibility.
A
Arjen Poutsma 已提交
530
	 * @param annotation the annotation to retrieve the attributes for
J
Juergen Hoeller 已提交
531 532
	 * @return the Map of annotation attributes, with attribute names as keys and
	 * corresponding attribute values as values
A
Arjen Poutsma 已提交
533 534
	 */
	public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
535
		return getAnnotationAttributes(annotation, false, false);
536 537 538
	}

	/**
J
Juergen Hoeller 已提交
539 540
	 * Retrieve the given annotation's attributes as a {@link Map}. Equivalent to
	 * calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with
541
	 * the {@code nestedAnnotationsAsMap} parameter set to {@code false}.
J
Juergen Hoeller 已提交
542 543
	 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
	 * However, the {@code Map} signature has been preserved for binary compatibility.
544
	 * @param annotation the annotation to retrieve the attributes for
545
	 * @param classValuesAsString whether to turn Class references into Strings (for
J
Juergen Hoeller 已提交
546 547
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
	 * or to preserve them as Class references
J
Juergen Hoeller 已提交
548 549
	 * @return the Map of annotation attributes, with attribute names as keys and
	 * corresponding attribute values as values
550
	 */
551
	public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
552 553 554 555 556
		return getAnnotationAttributes(annotation, classValuesAsString, false);
	}

	/**
	 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes}
J
Juergen Hoeller 已提交
557 558 559
	 * map structure.
	 * <p>This method provides fully recursive annotation reading capabilities on par with
	 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
560 561
	 * @param annotation the annotation to retrieve the attributes for
	 * @param classValuesAsString whether to turn Class references into Strings (for
J
Juergen Hoeller 已提交
562 563
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
	 * or to preserve them as Class references
564 565 566 567 568 569 570 571
	 * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
	 * Annotation instances
	 * @return the annotation attributes (a specialized Map) with attribute names as keys
	 * and corresponding attribute values as values
	 * @since 3.1.1
	 */
572 573
	public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,
			boolean nestedAnnotationsAsMap) {
574 575

		AnnotationAttributes attrs = new AnnotationAttributes();
A
Arjen Poutsma 已提交
576
		Method[] methods = annotation.annotationType().getDeclaredMethods();
577
		for (Method method : methods) {
A
Arjen Poutsma 已提交
578 579
			if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
				try {
580
					Object value = method.invoke(annotation);
581
					attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
A
Arjen Poutsma 已提交
582 583 584 585 586 587 588 589 590
				}
				catch (Exception ex) {
					throw new IllegalStateException("Could not obtain annotation attribute values", ex);
				}
			}
		}
		return attrs;
	}

591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
	/**
	 * Adapt the given value according to the given class and nested annotation settings.
	 * @param value the annotation attribute value
	 * @param classValuesAsString whether to turn Class references into Strings (for
	 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
	 * or to preserve them as Class references
	 * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
	 * {@link AnnotationAttributes} maps (for compatibility with
	 * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
	 * Annotation instances
	 * @return the adapted value, or the original value if no adaptation is needed
	 */
	static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
		if (classValuesAsString) {
			if (value instanceof Class) {
				value = ((Class<?>) value).getName();
			}
			else if (value instanceof Class[]) {
				Class<?>[] clazzArray = (Class[]) value;
				String[] newValue = new String[clazzArray.length];
				for (int i = 0; i < clazzArray.length; i++) {
					newValue[i] = clazzArray[i].getName();
				}
				value = newValue;
			}
		}
		if (nestedAnnotationsAsMap && value instanceof Annotation) {
			return getAnnotationAttributes((Annotation) value, classValuesAsString, true);
		}
		else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
			Annotation[] realAnnotations = (Annotation[]) value;
			AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
			for (int i = 0; i < realAnnotations.length; i++) {
				mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
			}
			return mappedAnnotations;
		}
		else {
			return value;
		}
	}

A
Arjen Poutsma 已提交
633
	/**
634
	 * Retrieve the <em>value</em> of the {@code &quot;value&quot;} attribute of a
J
Juergen Hoeller 已提交
635
	 * single-element Annotation, given an annotation instance.
A
Arjen Poutsma 已提交
636
	 * @param annotation the annotation instance from which to retrieve the value
637
	 * @return the attribute value, or {@code null} if not found
A
Arjen Poutsma 已提交
638 639 640 641 642 643 644
	 * @see #getValue(Annotation, String)
	 */
	public static Object getValue(Annotation annotation) {
		return getValue(annotation, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
645
	 * Retrieve the <em>value</em> of a named attribute, given an annotation instance.
A
Arjen Poutsma 已提交
646 647
	 * @param annotation the annotation instance from which to retrieve the value
	 * @param attributeName the name of the attribute value to retrieve
648
	 * @return the attribute value, or {@code null} if not found
J
Juergen Hoeller 已提交
649
	 * @see #getValue(Annotation)
A
Arjen Poutsma 已提交
650 651 652
	 */
	public static Object getValue(Annotation annotation, String attributeName) {
		try {
J
Juergen Hoeller 已提交
653
			Method method = annotation.annotationType().getDeclaredMethod(attributeName);
654
			ReflectionUtils.makeAccessible(method);
A
Arjen Poutsma 已提交
655 656 657 658 659 660 661 662
			return method.invoke(annotation);
		}
		catch (Exception ex) {
			return null;
		}
	}

	/**
663
	 * Retrieve the <em>default value</em> of the {@code &quot;value&quot;} attribute
J
Juergen Hoeller 已提交
664 665
	 * of a single-element Annotation, given an annotation instance.
	 * @param annotation the annotation instance from which to retrieve the default value
666
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
667 668 669 670 671 672 673
	 * @see #getDefaultValue(Annotation, String)
	 */
	public static Object getDefaultValue(Annotation annotation) {
		return getDefaultValue(annotation, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
674
	 * Retrieve the <em>default value</em> of a named attribute, given an annotation instance.
J
Juergen Hoeller 已提交
675
	 * @param annotation the annotation instance from which to retrieve the default value
A
Arjen Poutsma 已提交
676
	 * @param attributeName the name of the attribute value to retrieve
677
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
678 679 680 681 682 683 684
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Annotation annotation, String attributeName) {
		return getDefaultValue(annotation.annotationType(), attributeName);
	}

	/**
685
	 * Retrieve the <em>default value</em> of the {@code &quot;value&quot;} attribute
J
Juergen Hoeller 已提交
686 687
	 * 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
688
	 * @return the default value, or {@code null} if not found
A
Arjen Poutsma 已提交
689 690 691 692 693 694 695
	 * @see #getDefaultValue(Class, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
		return getDefaultValue(annotationType, VALUE);
	}

	/**
J
Juergen Hoeller 已提交
696 697
	 * Retrieve the <em>default value</em> of a named attribute, given the
	 * {@link Class annotation type}.
J
Juergen Hoeller 已提交
698
	 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved
A
Arjen Poutsma 已提交
699
	 * @param attributeName the name of the attribute value to retrieve.
700
	 * @return the default value of the named attribute, or {@code null} if not found
A
Arjen Poutsma 已提交
701 702 703 704
	 * @see #getDefaultValue(Annotation, String)
	 */
	public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
		try {
J
Juergen Hoeller 已提交
705
			return annotationType.getDeclaredMethod(attributeName).getDefaultValue();
A
Arjen Poutsma 已提交
706 707 708 709 710 711
		}
		catch (Exception ex) {
			return null;
		}
	}

712

713
	/**
714
	 * Cache key for the AnnotatedElement cache.
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
	 */
	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());
		}
	}


747 748 749 750 751 752 753 754 755 756
	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 已提交
757
		public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
			this.containerAnnotationType = containerAnnotationType;
			this.annotationType = annotationType;
		}

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

		@SuppressWarnings("unchecked")
		private void process(AnnotatedElement annotatedElement) {
			if (this.visited.add(annotatedElement)) {
				for (Annotation annotation : annotatedElement.getAnnotations()) {
					if (ObjectUtils.nullSafeEquals(this.annotationType, annotation.annotationType())) {
						this.result.add((A) annotation);
					}
					else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.annotationType())) {
775
						this.result.addAll(getValue(annotation));
776
					}
777
					else if (!isInJavaLangAnnotationPackage(annotation)) {
778 779 780 781 782 783 784
						process(annotation.annotationType());
					}
				}
			}
		}

		@SuppressWarnings("unchecked")
785
		private List<A> getValue(Annotation annotation) {
786 787
			try {
				Method method = annotation.annotationType().getDeclaredMethod("value");
788
				ReflectionUtils.makeAccessible(method);
789
				return Arrays.asList((A[]) method.invoke(annotation));
790 791
			}
			catch (Exception ex) {
792 793
				// Unable to read value from repeating annotation container -> ignore it.
				return Collections.emptyList();
794 795 796
			}
		}
	}
J
Juergen Hoeller 已提交
797

A
Arjen Poutsma 已提交
798
}