Skip to content

Commit 49fccfb

Browse files
committed
Consistent retrieval of javax annotation types (e.g. JSR-305)
Closes gh-22696
1 parent f9d5957 commit 49fccfb

File tree

6 files changed

+49
-36
lines changed

6 files changed

+49
-36
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMappings.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ AnnotationTypeMapping get(int index) {
156156
return this.mappings.get(index);
157157
}
158158

159+
159160
/**
160161
* Return {@link AnnotationTypeMappings} for the specified annotation type.
161162
* @param annotationType the source annotation type
@@ -202,8 +203,7 @@ private static class Cache {
202203
}
203204

204205
/**
205-
* Return or create {@link AnnotationTypeMappings} for the specified
206-
* annotation type.
206+
* Return or create {@link AnnotationTypeMappings} for the specified annotation type.
207207
* @param annotationType the annotation type
208208
* @return a new or existing {@link AnnotationTypeMapping} instance
209209
*/
@@ -214,7 +214,6 @@ AnnotationTypeMappings get(Class<? extends Annotation> annotationType) {
214214
AnnotationTypeMappings createMappings(Class<? extends Annotation> annotationType) {
215215
return new AnnotationTypeMappings(this.filter, annotationType);
216216
}
217-
218217
}
219218

220219
}

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,14 @@ public static boolean isCandidateClass(Class<?> clazz, Class<? extends Annotatio
163163
* @since 5.2
164164
*/
165165
public static boolean isCandidateClass(Class<?> clazz, String annotationName) {
166-
return !clazz.getName().startsWith("java");
166+
if (annotationName.startsWith("java.")) {
167+
return true;
168+
}
169+
if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
170+
return false;
171+
}
172+
// TODO: annotation presence registry to be integrated here
173+
return true;
167174
}
168175

169176
/**

spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java

+11-17
Original file line numberDiff line numberDiff line change
@@ -462,18 +462,18 @@ static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defe
462462
return annotations.clone();
463463
}
464464

465-
private static boolean isIgnorable(Class<?> annotationType) {
466-
return AnnotationFilter.PLAIN.matches(annotationType);
467-
}
468-
469465
private static <C> boolean isFiltered(
470466
Class<?> sourceClass, C context, @Nullable BiPredicate<C, Class<?>> classFilter) {
471467

472468
return (classFilter != null && classFilter.test(context, sourceClass));
473469
}
474470

475-
static boolean isKnownEmpty(AnnotatedElement source,SearchStrategy searchStrategy, AnnotationFilter annotationFilter) {
476-
if (annotationFilter == AnnotationFilter.PLAIN && hasPlainJavaAnnotationsOnly(source)) {
471+
private static boolean isIgnorable(Class<?> annotationType) {
472+
return AnnotationFilter.PLAIN.matches(annotationType);
473+
}
474+
475+
static boolean isKnownEmpty(AnnotatedElement source, SearchStrategy searchStrategy) {
476+
if (hasPlainJavaAnnotationsOnly(source)) {
477477
return true;
478478
}
479479
if (searchStrategy == SearchStrategy.DIRECT || isWithoutHierarchy(source)) {
@@ -486,25 +486,19 @@ static boolean isKnownEmpty(AnnotatedElement source,SearchStrategy searchStrateg
486486
}
487487

488488
static boolean hasPlainJavaAnnotationsOnly(@Nullable Object annotatedElement) {
489-
Class<?> type;
490489
if (annotatedElement instanceof Class) {
491-
type = (Class<?>) annotatedElement;
490+
return hasPlainJavaAnnotationsOnly((Class<?>) annotatedElement);
492491
}
493492
else if (annotatedElement instanceof Member) {
494-
type = ((Member) annotatedElement).getDeclaringClass();
493+
return hasPlainJavaAnnotationsOnly(((Member) annotatedElement).getDeclaringClass());
495494
}
496495
else {
497496
return false;
498497
}
498+
}
499499

500-
if (type == Ordered.class) {
501-
return true;
502-
}
503-
String name = type.getName();
504-
return (name.startsWith("java") ||
505-
name.startsWith("org.springframework.lang.") ||
506-
name.startsWith("org.springframework.util.") ||
507-
(name.startsWith("com.sun") && !name.contains("Proxy")));
500+
static boolean hasPlainJavaAnnotationsOnly(Class<?> type) {
501+
return (type.getName().startsWith("java.") || type == Ordered.class);
508502
}
509503

510504
private static boolean isWithoutHierarchy(AnnotatedElement source) {

spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,8 @@ private static Integer findOrder(MergedAnnotations annotations) {
127127
*/
128128
@Nullable
129129
public static Integer getPriority(Class<?> type) {
130-
return MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE).get(
131-
JAVAX_PRIORITY_ANNOTATION).getValue(MergedAnnotation.VALUE,
132-
Integer.class).orElse(null);
130+
return MergedAnnotations.from(type, SearchStrategy.EXHAUSTIVE).get(JAVAX_PRIORITY_ANNOTATION)
131+
.getValue(MergedAnnotation.VALUE, Integer.class).orElse(null);
133132
}
134133

135134
}

spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotations.java

+5-8
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ private <C, R> R scan(C criteria, AnnotationsProcessor<C, R> processor) {
244244
}
245245

246246

247-
static MergedAnnotations from(@Nullable AnnotatedElement element, SearchStrategy searchStrategy,
247+
static MergedAnnotations from(AnnotatedElement element, SearchStrategy searchStrategy,
248248
RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {
249249

250-
if (element == null || AnnotationsScanner.isKnownEmpty(element, searchStrategy, annotationFilter)) {
250+
if (AnnotationsScanner.isKnownEmpty(element, searchStrategy)) {
251251
return NONE;
252252
}
253253
return new TypeMappedAnnotations(element, searchStrategy, repeatableContainers, annotationFilter);
@@ -262,12 +262,9 @@ static MergedAnnotations from(@Nullable Object source, Annotation[] annotations,
262262
return new TypeMappedAnnotations(source, annotations, repeatableContainers, annotationFilter);
263263
}
264264

265-
private static boolean isMappingForType(@Nullable AnnotationTypeMapping mapping,
265+
private static boolean isMappingForType(AnnotationTypeMapping mapping,
266266
AnnotationFilter annotationFilter, @Nullable Object requiredType) {
267267

268-
if (mapping == null) {
269-
return false;
270-
}
271268
Class<? extends Annotation> actualType = mapping.getAnnotationType();
272269
return (!annotationFilter.matches(actualType) &&
273270
(requiredType == null || actualType == requiredType || actualType.getName().equals(requiredType)));
@@ -523,7 +520,7 @@ int size() {
523520
@Nullable
524521
AnnotationTypeMapping getMapping(int annotationIndex, int mappingIndex) {
525522
AnnotationTypeMappings mappings = getMappings(annotationIndex);
526-
return mappingIndex < mappings.size() ? mappings.get(mappingIndex) : null;
523+
return (mappingIndex < mappings.size() ? mappings.get(mappingIndex) : null);
527524
}
528525

529526
AnnotationTypeMappings getMappings(int annotationIndex) {
@@ -612,7 +609,7 @@ private AnnotationTypeMapping getNextSuitableMapping(Aggregate aggregate, int an
612609
AnnotationTypeMapping mapping;
613610
do {
614611
mapping = aggregate.getMapping(annotationIndex, cursors[annotationIndex]);
615-
if (isMappingForType(mapping, annotationFilter, this.requiredType)) {
612+
if (mapping != null && isMappingForType(mapping, annotationFilter, this.requiredType)) {
616613
return mapping;
617614
}
618615
cursors[annotationIndex]++;

spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import javax.annotation.Nonnull;
3333
import javax.annotation.ParametersAreNonnullByDefault;
3434
import javax.annotation.Resource;
35+
import javax.annotation.meta.When;
3536

3637
import org.junit.Ignore;
3738
import org.junit.Rule;
@@ -198,7 +199,7 @@ public void getAllAnnotationAttributesOnNonAnnotatedClass() {
198199
public void getAllAnnotationAttributesOnClassWithLocalAnnotation() {
199200
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(TxConfig.class, TX_NAME);
200201
assertNotNull("Annotation attributes map for @Transactional on TxConfig", attributes);
201-
assertEquals("value for TxConfig.", asList("TxConfig"), attributes.get("value"));
202+
assertEquals("value for TxConfig", asList("TxConfig"), attributes.get("value"));
202203
}
203204

204205
@Test
@@ -234,7 +235,7 @@ public void getAllAnnotationAttributesFavorsInheritedComposedAnnotationsOverMore
234235
public void getAllAnnotationAttributesOnClassWithLocalAnnotationThatShadowsAnnotationFromSuperclass() {
235236
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(DerivedTxConfig.class, TX_NAME);
236237
assertNotNull("Annotation attributes map for @Transactional on DerivedTxConfig", attributes);
237-
assertEquals("value for DerivedTxConfig.", asList("DerivedTxConfig"), attributes.get("value"));
238+
assertEquals("value for DerivedTxConfig", asList("DerivedTxConfig"), attributes.get("value"));
238239
}
239240

240241
/**
@@ -249,13 +250,29 @@ public void getAllAnnotationAttributesOnClassWithMultipleComposedAnnotations() {
249250
attributes.get("value"));
250251
}
251252

253+
@Test
254+
public void getAllAnnotationAttributesOnLangType() {
255+
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(
256+
NonNullApi.class, Nonnull.class.getName());
257+
assertNotNull("Annotation attributes map for @Nonnull on NonNullApi", attributes);
258+
assertEquals("value for NonNullApi", asList(When.ALWAYS), attributes.get("when"));
259+
}
260+
261+
@Test
262+
public void getAllAnnotationAttributesOnJavaxType() {
263+
MultiValueMap<String, Object> attributes = getAllAnnotationAttributes(
264+
ParametersAreNonnullByDefault.class, Nonnull.class.getName());
265+
assertNotNull("Annotation attributes map for @Nonnull on NonNullApi", attributes);
266+
assertEquals("value for NonNullApi", asList(When.ALWAYS), attributes.get("when"));
267+
}
268+
252269
@Test
253270
public void getMergedAnnotationAttributesOnClassWithLocalAnnotation() {
254271
Class<?> element = TxConfig.class;
255272
String name = TX_NAME;
256273
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
257274
assertNotNull("Annotation attributes for @Transactional on TxConfig", attributes);
258-
assertEquals("value for TxConfig.", "TxConfig", attributes.getString("value"));
275+
assertEquals("value for TxConfig", "TxConfig", attributes.getString("value"));
259276
// Verify contracts between utility methods:
260277
assertTrue(isAnnotated(element, name));
261278
}
@@ -266,7 +283,7 @@ public void getMergedAnnotationAttributesOnClassWithLocalAnnotationThatShadowsAn
266283
String name = TX_NAME;
267284
AnnotationAttributes attributes = getMergedAnnotationAttributes(element, name);
268285
assertNotNull("Annotation attributes for @Transactional on DerivedTxConfig", attributes);
269-
assertEquals("value for DerivedTxConfig.", "DerivedTxConfig", attributes.getString("value"));
286+
assertEquals("value for DerivedTxConfig", "DerivedTxConfig", attributes.getString("value"));
270287
// Verify contracts between utility methods:
271288
assertTrue(isAnnotated(element, name));
272289
}

0 commit comments

Comments
 (0)