Consistent retrieval of javax annotation types (e.g. JSR-305)

Closes gh-22696
This commit is contained in:
Juergen Hoeller 2019-03-28 14:38:55 +01:00
parent f9d59570de
commit 49fccfb158
6 changed files with 49 additions and 36 deletions

View File

@ -156,6 +156,7 @@ final class AnnotationTypeMappings {
return this.mappings.get(index); return this.mappings.get(index);
} }
/** /**
* Return {@link AnnotationTypeMappings} for the specified annotation type. * Return {@link AnnotationTypeMappings} for the specified annotation type.
* @param annotationType the source annotation type * @param annotationType the source annotation type
@ -202,8 +203,7 @@ final class AnnotationTypeMappings {
} }
/** /**
* Return or create {@link AnnotationTypeMappings} for the specified * Return or create {@link AnnotationTypeMappings} for the specified annotation type.
* annotation type.
* @param annotationType the annotation type * @param annotationType the annotation type
* @return a new or existing {@link AnnotationTypeMapping} instance * @return a new or existing {@link AnnotationTypeMapping} instance
*/ */
@ -214,7 +214,6 @@ final class AnnotationTypeMappings {
AnnotationTypeMappings createMappings(Class<? extends Annotation> annotationType) { AnnotationTypeMappings createMappings(Class<? extends Annotation> annotationType) {
return new AnnotationTypeMappings(this.filter, annotationType); return new AnnotationTypeMappings(this.filter, annotationType);
} }
} }
} }

View File

@ -163,7 +163,14 @@ public abstract class AnnotationUtils {
* @since 5.2 * @since 5.2
*/ */
public static boolean isCandidateClass(Class<?> clazz, String annotationName) { public static boolean isCandidateClass(Class<?> clazz, String annotationName) {
return !clazz.getName().startsWith("java"); if (annotationName.startsWith("java.")) {
return true;
}
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
return false;
}
// TODO: annotation presence registry to be integrated here
return true;
} }
/** /**

View File

@ -462,18 +462,18 @@ abstract class AnnotationsScanner {
return annotations.clone(); return annotations.clone();
} }
private static boolean isIgnorable(Class<?> annotationType) {
return AnnotationFilter.PLAIN.matches(annotationType);
}
private static <C> boolean isFiltered( private static <C> boolean isFiltered(
Class<?> sourceClass, C context, @Nullable BiPredicate<C, Class<?>> classFilter) { Class<?> sourceClass, C context, @Nullable BiPredicate<C, Class<?>> classFilter) {
return (classFilter != null && classFilter.test(context, sourceClass)); return (classFilter != null && classFilter.test(context, sourceClass));
} }
static boolean isKnownEmpty(AnnotatedElement source,SearchStrategy searchStrategy, AnnotationFilter annotationFilter) { private static boolean isIgnorable(Class<?> annotationType) {
if (annotationFilter == AnnotationFilter.PLAIN && hasPlainJavaAnnotationsOnly(source)) { return AnnotationFilter.PLAIN.matches(annotationType);
}
static boolean isKnownEmpty(AnnotatedElement source, SearchStrategy searchStrategy) {
if (hasPlainJavaAnnotationsOnly(source)) {
return true; return true;
} }
if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source)) { if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source)) {
@ -486,25 +486,19 @@ abstract class AnnotationsScanner {
} }
static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) { static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) {
Class<?> type;
if (annotatedElement instanceof Class) { if (annotatedElement instanceof Class) {
type = (Class<?>) annotatedElement; return hasPlainJavaAnnotationsOnly((Class<?>) annotatedElement);
} }
else if (annotatedElement instanceof Member) { else if (annotatedElement instanceof Member) {
type = ((Member) annotatedElement).getDeclaringClass(); return hasPlainJavaAnnotationsOnly(((Member) annotatedElement).getDeclaringClass());
} }
else { else {
return false; return false;
} }
}
if (type == Ordered.class) { static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
return true; return (type.getName().startsWith("java.") || type == Ordered.class);
}
String name = type.getName();
return (name.startsWith("java") ||
name.startsWith("org.springframework.lang.") ||
name.startsWith("org.springframework.util.") ||
(name.startsWith("com.sun") && !name.contains("Proxy")));
} }
private static boolean isWithoutHierarchy(AnnotatedElement source) { private static boolean isWithoutHierarchy(AnnotatedElement source) {

View File

@ -127,9 +127,8 @@ public abstract class OrderUtils {
*/ */
@Nullable @Nullable
public static Integer getPriority(Class<?> type) { public static Integer getPriority(Class<?> type) {
return MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE).get( return MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE).get(JAVAX_PRIORITY_ANNOTATION)
JAVAX_PRIORITY_ANNOTATION).getValue(MergedAnnotation.VALUE, .getValue(MergedAnnotation.VALUE, Integer.class).orElse(null);
Integer.class).orElse(null);
} }
} }

View File

@ -244,10 +244,10 @@ final class TypeMappedAnnotations implements MergedAnnotations {
} }
static MergedAnnotations from(@Nullable AnnotatedElement element, SearchStrategy searchStrategy, static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) { RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
if (element == null || AnnotationsScanner.isKnownEmpty(element, searchStrategy, annotationFilter)) { if (AnnotationsScanner.isKnownEmpty(element, searchStrategy)) {
return NONE; return NONE;
} }
return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers, annotationFilter); return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers, annotationFilter);
@ -262,12 +262,9 @@ final class TypeMappedAnnotations implements MergedAnnotations {
return new TypeMappedAnnotations(source, annotations, repeatableContainers, annotationFilter); return new TypeMappedAnnotations(source, annotations, repeatableContainers, annotationFilter);
} }
private static boolean isMappingForType(@Nullable AnnotationTypeMapping mapping, private static boolean isMappingForType(AnnotationTypeMapping mapping,
AnnotationFilter annotationFilter, @Nullable Object requiredType) { AnnotationFilter annotationFilter, @Nullable Object requiredType) {
if (mapping == null) {
return false;
}
Class<? extends Annotation> actualType = mapping.getAnnotationType(); Class<? extends Annotation> actualType = mapping.getAnnotationType();
return (!annotationFilter.matches(actualType) && return (!annotationFilter.matches(actualType) &&
(requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType))); (requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType)));
@ -523,7 +520,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
@Nullable @Nullable
AnnotationTypeMapping getMapping(int annotationIndex, int mappingIndex) { AnnotationTypeMapping getMapping(int annotationIndex, int mappingIndex) {
AnnotationTypeMappings mappings = getMappings(annotationIndex); AnnotationTypeMappings mappings = getMappings(annotationIndex);
return mappingIndex < mappings.size() ? mappings.get(mappingIndex) : null; return (mappingIndex < mappings.size() ? mappings.get(mappingIndex) : null);
} }
AnnotationTypeMappings getMappings(int annotationIndex) { AnnotationTypeMappings getMappings(int annotationIndex) {
@ -612,7 +609,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
AnnotationTypeMapping mapping; AnnotationTypeMapping mapping;
do { do {
mapping = aggregate.getMapping(annotationIndex, cursors[annotationIndex]); mapping = aggregate.getMapping(annotationIndex, cursors[annotationIndex]);
if (isMappingForType(mapping, annotationFilter, this.requiredType)) { if (mapping != null && isMappingForType(mapping, annotationFilter, this.requiredType)) {
return mapping; return mapping;
} }
cursors[annotationIndex]++; cursors[annotationIndex]++;

View File

@ -32,6 +32,7 @@ import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.annotation.meta.When;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
@ -198,7 +199,7 @@ public class AnnotatedElementUtilsTests {
public void getAllAnnotationAttributesOnClassWithLocalAnnotation() { public void getAllAnnotationAttributesOnClassWithLocalAnnotation() {
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(TxConfig.class, TX_NAME); MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(TxConfig.class, TX_NAME);
assertNotNull("Annotation attributes map for @Transactional on TxConfig", attributes); assertNotNull("Annotation attributes map for @Transactional on TxConfig", attributes);
assertEquals("value for TxConfig.", asList("TxConfig"), attributes.get("value")); assertEquals("value for TxConfig", asList("TxConfig"), attributes.get("value"));
} }
@Test @Test
@ -234,7 +235,7 @@ public class AnnotatedElementUtilsTests {
public void getAllAnnotationAttributesOnClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() { public void getAllAnnotationAttributesOnClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() {
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(DerivedTxConfig.class, TX_NAME); MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(DerivedTxConfig.class, TX_NAME);
assertNotNull("Annotation attributes map for @Transactional on DerivedTxConfig", attributes); assertNotNull("Annotation attributes map for @Transactional on DerivedTxConfig", attributes);
assertEquals("value for DerivedTxConfig.", asList("DerivedTxConfig"), attributes.get("value")); assertEquals("value for DerivedTxConfig", asList("DerivedTxConfig"), attributes.get("value"));
} }
/** /**
@ -249,13 +250,29 @@ public class AnnotatedElementUtilsTests {
attributes.get("value")); attributes.get("value"));
} }
@Test
public void getAllAnnotationAttributesOnLangType() {
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(
NonNullApi.class, Nonnull.class.getName());
assertNotNull("Annotation attributes map for @Nonnull on NonNullApi", attributes);
assertEquals("value for NonNullApi", asList(When.ALWAYS), attributes.get("when"));
}
@Test
public void getAllAnnotationAttributesOnJavaxType() {
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(
ParametersAreNonnullByDefault.class, Nonnull.class.getName());
assertNotNull("Annotation attributes map for @Nonnull on NonNullApi", attributes);
assertEquals("value for NonNullApi", asList(When.ALWAYS), attributes.get("when"));
}
@Test @Test
public void getMergedAnnotationAttributesOnClassWithLocalAnnotation() { public void getMergedAnnotationAttributesOnClassWithLocalAnnotation() {
Class<?> element = TxConfig.class; Class<?> element = TxConfig.class;
String name = TX_NAME; String name = TX_NAME;
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name); AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
assertNotNull("Annotation attributes for @Transactional on TxConfig", attributes); assertNotNull("Annotation attributes for @Transactional on TxConfig", attributes);
assertEquals("value for TxConfig.", "TxConfig", attributes.getString("value")); assertEquals("value for TxConfig", "TxConfig", attributes.getString("value"));
// Verify contracts between utility methods: // Verify contracts between utility methods:
assertTrue(isAnnotated(element, name)); assertTrue(isAnnotated(element, name));
} }
@ -266,7 +283,7 @@ public class AnnotatedElementUtilsTests {
String name = TX_NAME; String name = TX_NAME;
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name); AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
assertNotNull("Annotation attributes for @Transactional on DerivedTxConfig", attributes); assertNotNull("Annotation attributes for @Transactional on DerivedTxConfig", attributes);
assertEquals("value for DerivedTxConfig.", "DerivedTxConfig", attributes.getString("value")); assertEquals("value for DerivedTxConfig", "DerivedTxConfig", attributes.getString("value"));
// Verify contracts between utility methods: // Verify contracts between utility methods:
assertTrue(isAnnotated(element, name)); assertTrue(isAnnotated(element, name));
} }