AnnotationUtils favors local composed annotations over interface annotations and consistently logs introspection failures via lazily initialized logger

Issue: SPR-12355
Issue: SPR-12325
Issue: SPR-12329
This commit is contained in:
Juergen Hoeller 2014-10-20 21:47:47 +02:00
parent 77a62ec8b8
commit 716916b281
2 changed files with 61 additions and 63 deletions

View File

@ -66,7 +66,6 @@ public abstract class AnnotationUtils {
/** The attribute name for annotations with a single element */
public static final String VALUE = "value";
private static final Log logger = LogFactory.getLog(AnnotationUtils.class);
private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache =
new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256);
@ -74,6 +73,8 @@ public abstract class AnnotationUtils {
private static final Map<Class<?>, Boolean> annotatedInterfaceCache =
new ConcurrentReferenceHashMap<Class<?>, Boolean>(256);
private static transient Log logger;
/**
* Get a single {@link Annotation} of {@code annotationType} from the supplied
@ -93,10 +94,7 @@ public abstract class AnnotationUtils {
}
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 [" + ann.annotationType() + "]: " + ex);
}
logIntrospectionFailure(ann.annotationType(), ex);
return null;
}
}
@ -125,10 +123,7 @@ public abstract class AnnotationUtils {
}
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 [" + annotatedElement + "]: " + ex);
}
logIntrospectionFailure(annotatedElement, ex);
return null;
}
}
@ -146,10 +141,7 @@ public abstract class AnnotationUtils {
}
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 [" + method + "]: " + ex);
}
logIntrospectionFailure(method, ex);
return null;
}
}
@ -208,10 +200,7 @@ public abstract class AnnotationUtils {
}
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 [" + annotatedElement + "]: " + ex);
}
logIntrospectionFailure(annotatedElement, ex);
}
return Collections.emptySet();
}
@ -293,10 +282,7 @@ public abstract class AnnotationUtils {
}
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);
}
logIntrospectionFailure(ifcMethod, ex);
}
}
annotatedInterfaceCache.put(iface, found);
@ -347,35 +333,39 @@ public abstract class AnnotationUtils {
* @param visited the set of annotations that have already been visited
* @return the annotation if found, or {@code null} if not found
*/
@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
Assert.notNull(clazz, "Class must not be null");
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
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.
if (logger.isInfoEnabled()) {
logger.info("Failed to introspect annotations on [" + clazz + "]: " + ex);
try {
Annotation[] anns = clazz.getDeclaredAnnotations();
for (Annotation ann : anns) {
if (ann.annotationType().equals(annotationType)) {
return (A) ann;
}
}
for (Annotation ann : anns) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
if (annotation != null) {
return annotation;
}
}
return null;
}
}
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.
return null;
}
for (Class<?> ifc : clazz.getInterfaces()) {
A annotation = findAnnotation(ifc, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
for (Annotation ann : clazz.getDeclaredAnnotations()) {
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || superclass.equals(Object.class)) {
return null;
@ -473,8 +463,8 @@ public abstract class AnnotationUtils {
Assert.notNull(clazz, "Class must not be null");
boolean declaredLocally = false;
try {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(annotationType)) {
for (Annotation ann : clazz.getDeclaredAnnotations()) {
if (ann.annotationType().equals(annotationType)) {
declaredLocally = true;
break;
}
@ -482,10 +472,7 @@ public abstract class AnnotationUtils {
}
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 [" + clazz + "]: " + ex);
}
logIntrospectionFailure(clazz, ex);
}
return declaredLocally;
}
@ -710,6 +697,18 @@ public abstract class AnnotationUtils {
}
private static void logIntrospectionFailure(AnnotatedElement annotatedElement, Exception ex) {
Log loggerToUse = logger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(AnnotationUtils.class);
logger = loggerToUse;
}
if (loggerToUse.isInfoEnabled()) {
loggerToUse.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
}
}
/**
* Cache key for the AnnotatedElement cache.
*/
@ -767,15 +766,15 @@ public abstract class AnnotationUtils {
@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);
for (Annotation ann : annotatedElement.getAnnotations()) {
if (ObjectUtils.nullSafeEquals(this.annotationType, ann.annotationType())) {
this.result.add((A) ann);
}
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.annotationType())) {
this.result.addAll(getValue(annotation));
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, ann.annotationType())) {
this.result.addAll(getValue(ann));
}
else if (!isInJavaLangAnnotationPackage(annotation)) {
process(annotation.annotationType());
else if (!isInJavaLangAnnotationPackage(ann)) {
process(ann.annotationType());
}
}
}

View File

@ -99,16 +99,15 @@ public class AnnotationUtilsTests {
// assertNotNull(o);
// }
/**
* @since 4.1.2
*/
@Test
public void findAnnotationFavorsInterfacesOverLocalMetaAnnotations() {
public void findAnnotationFavorsLocalMetaAnnotationsOverInterfaces() {
Component component = AnnotationUtils.findAnnotation(
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
assertNotNull(component);
// By inspecting ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface, one
// might expect that "meta2" should be found; however, with the current
// implementation "meta1" will be found.
assertEquals("meta1", component.value());
assertEquals("meta2", component.value());
}
/**
@ -116,8 +115,8 @@ public class AnnotationUtilsTests {
*/
@Test
public void findAnnotationFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
Transactional transactional = AnnotationUtils.findAnnotation(SubSubClassWithInheritedAnnotation.class,
Transactional.class);
Transactional transactional = AnnotationUtils.findAnnotation(
SubSubClassWithInheritedAnnotation.class, Transactional.class);
assertNotNull(transactional);
assertTrue("readOnly flag for SubSubClassWithInheritedAnnotation", transactional.readOnly());
}
@ -127,8 +126,8 @@ public class AnnotationUtilsTests {
*/
@Test
public void findAnnotationFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
Component component = AnnotationUtils.findAnnotation(SubSubClassWithInheritedMetaAnnotation.class,
Component.class);
Component component = AnnotationUtils.findAnnotation(
SubSubClassWithInheritedMetaAnnotation.class, Component.class);
assertNotNull(component);
assertEquals("meta2", component.value());
}