-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Description
Sam Brannen opened SPR-11475 and commented
Status Quo
The implementations of both AnnotationUtils and AnnotatedElementUtils (and possibly AbstractRecursiveAnnotationVisitor as well) currently favor inherited annotations and inherited composed annotations over composed annotations that are declared closer to the starting class passed to the findAnnotation() and getAnnotationAttributes() methods.
Given a class hierarchy with a depth of at least three, if the lowest level (e.g., Level3) is not directly annotated but Level2 (a direct superclass of Level3) is directly annotated with @ComposedAnno (which is meta-annotated with @Anno) and Level1 (a direct superclass of @Level2) is directly annotated with either @Anno or a composed annotation that is meta-annotated with @Anno, if the @ComposedAnno annotation is not declared as @Inherited, then any attributes declared via @Anno on @ComposedAnno (present on class Level2) will be shadowed by those declared via @Anno on class Level1.
This behavior is very non-intuitive and would likely be considered a bug by any developers who encounter it.
Concrete Example
Given...
@Component(value = "composed1")
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Composed1 {}
@Component(value = "composed2")
@Retention(RetentionPolicy.RUNTIME)
@interface Composed2 {}
@Composed1
class Level1 {}
@Composed2
class Level2 extends Level1 {}
class Level3 extends Level2 {}If we execute the following unit test, one would likely expect that "composed2" should be found, since the immediate superclass is annotated with @Composed2; however, with the current implementation "composed1" will be found since @Composed1 is declared as @Inherited and therefore shadows @Composed2. As such, the test fails on the last line.
@Test
public void findAnnotationFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
Component component = AnnotationUtils.findAnnotation(Level3.class, Component.class);
assertNotNull(component);
assertEquals("composed2", component.value());
}Proposal
Refactor the affected implementations of AnnotationUtils and AnnotatedElementUtils so that more locally declared composed annotations are favored over inherited annotations and inherited composed annotations.
This can likely be achieved by using the getDeclaredAnnotation() and getDeclaredAnnotations() methods in java.lang.Class instead of the getAnnotation() and getAnnotations() which are currently being used in these utility classes.
Note that MetaAnnotationUtils already uses getDeclaredAnnotations().
Deliverables
- Refactor
AnnotationUtilsto usegetDeclaredAnnotation()andgetDeclaredAnnotations()where appropriate. - Refactor
AnnotatedElementUtilsto usegetDeclaredAnnotation()andgetDeclaredAnnotations()where appropriate.
Affects: 4.0 GA
Issue Links:
- Favor more locally declared composed annotations over inherited annotations [SPR-11598] #16221 Favor more locally declared composed annotations over inherited annotations ("is depended on by")
- Annotations on superclasses are detected by StandardAnnotationMetadata [SPR-11595] #16219 Annotations on superclasses are detected by StandardAnnotationMetadata
- Favor more locally declared composed annotations over interface annotations in AnnotationUtils [SPR-12355] #16960 Favor more locally declared composed annotations over interface annotations in AnnotationUtils
Referenced from: commits a2f1169, 0f5a27c, 1d30bf8, 0637864, 90b938a