Skip to content

Commit 90b938a

Browse files
committed
Verify status quo when searching for non-inherited composed annotations
Issue: SPR-11475
1 parent f895096 commit 90b938a

File tree

2 files changed

+160
-5
lines changed

2 files changed

+160
-5
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.core.annotation;
18+
19+
import java.lang.annotation.Inherited;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
23+
import org.junit.Test;
24+
25+
import static org.junit.Assert.*;
26+
27+
/**
28+
* Unit tests for {@link AnnotatedElementUtils}.
29+
*
30+
* @author Sam Brannen
31+
* @since 4.0.3
32+
*/
33+
public class AnnotatedElementUtilsTests {
34+
35+
@Test
36+
public void getAnnotationAttributesFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
37+
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(
38+
SubSubClassWithInheritedAnnotation.class, Transactional.class.getName());
39+
assertNotNull(attributes);
40+
41+
// By inspecting SubSubClassWithInheritedAnnotation, one might expect that the
42+
// readOnly flag should be true, since the immediate superclass is annotated with
43+
// @Composed2; however, with the current implementation the readOnly flag will be
44+
// false since @Transactional is declared as @Inherited.
45+
assertFalse("readOnly flag for SubSubClassWithInheritedAnnotation", attributes.getBoolean("readOnly"));
46+
}
47+
48+
@Test
49+
public void getAnnotationAttributesFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
50+
AnnotationAttributes attributes = AnnotatedElementUtils.getAnnotationAttributes(
51+
SubSubClassWithInheritedComposedAnnotation.class, Transactional.class.getName());
52+
assertNotNull(attributes);
53+
54+
// By inspecting SubSubClassWithInheritedComposedAnnotation, one might expect that
55+
// the readOnly flag should be true, since the immediate superclass is annotated
56+
// with @Composed2; however, with the current implementation the readOnly flag
57+
// will be false since @Composed1 is declared as @Inherited.
58+
assertFalse("readOnly flag", attributes.getBoolean("readOnly"));
59+
}
60+
61+
62+
// -------------------------------------------------------------------------
63+
64+
@Retention(RetentionPolicy.RUNTIME)
65+
@Inherited
66+
@interface Transactional {
67+
68+
boolean readOnly() default false;
69+
}
70+
71+
@Transactional
72+
@Retention(RetentionPolicy.RUNTIME)
73+
@Inherited
74+
@interface Composed1 {
75+
}
76+
77+
@Transactional(readOnly = true)
78+
@Retention(RetentionPolicy.RUNTIME)
79+
@interface Composed2 {
80+
}
81+
82+
@Transactional
83+
static class ClassWithInheritedAnnotation {
84+
}
85+
86+
@Composed2
87+
static class SubClassWithInheritedAnnotation extends ClassWithInheritedAnnotation {
88+
}
89+
90+
static class SubSubClassWithInheritedAnnotation extends SubClassWithInheritedAnnotation {
91+
}
92+
93+
@Composed1
94+
static class ClassWithInheritedComposedAnnotation {
95+
}
96+
97+
@Composed2
98+
static class SubClassWithInheritedComposedAnnotation extends ClassWithInheritedComposedAnnotation {
99+
}
100+
101+
static class SubSubClassWithInheritedComposedAnnotation extends SubClassWithInheritedComposedAnnotation {
102+
}
103+
104+
}

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

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,40 @@ public void findMethodAnnotationOnBridgeMethod() throws Exception {
100100
// }
101101

102102
@Test
103-
public void findAnnotationPrefersInterfacesOverLocalMetaAnnotations() {
103+
public void findAnnotationFavorsInterfacesOverLocalMetaAnnotations() {
104104
Component component = AnnotationUtils.findAnnotation(
105-
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
105+
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
106+
assertNotNull(component);
106107

107108
// By inspecting ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface, one
108109
// might expect that "meta2" should be found; however, with the current
109110
// implementation "meta1" will be found.
111+
assertEquals("meta1", component.value());
112+
}
113+
114+
@Test
115+
public void findAnnotationFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
116+
Transactional transactional = AnnotationUtils.findAnnotation(SubSubClassWithInheritedAnnotation.class,
117+
Transactional.class);
118+
assertNotNull(transactional);
119+
120+
// By inspecting SubSubClassWithInheritedAnnotation, one might expect that the
121+
// readOnly flag should be true, since the immediate superclass is annotated with
122+
// @Composed2; however, with the current implementation the readOnly flag will be
123+
// false since @Transactional is declared as @Inherited.
124+
assertFalse("readOnly flag for SubSubClassWithInheritedAnnotation", transactional.readOnly());
125+
}
126+
127+
@Test
128+
public void findAnnotationFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
129+
Component component = AnnotationUtils.findAnnotation(SubSubClassWithInheritedMetaAnnotation.class,
130+
Component.class);
110131
assertNotNull(component);
132+
133+
// By inspecting SubSubClassWithInheritedMetaAnnotation, one might expect that
134+
// "meta2" should be found, since the immediate superclass is annotated with
135+
// @Meta2; however, with the current implementation "meta1" will be found since
136+
// @Meta1 is declared as @Inherited.
111137
assertEquals("meta1", component.value());
112138
}
113139

@@ -350,14 +376,15 @@ public void getRepeatableFromMethod() throws Exception {
350376
}
351377

352378

353-
@Component(value="meta1")
379+
@Component(value = "meta1")
354380
@Order
355381
@Retention(RetentionPolicy.RUNTIME)
382+
@Inherited
356383
@interface Meta1 {
357384
}
358385

359-
@Component(value="meta2")
360-
@Transactional
386+
@Component(value = "meta2")
387+
@Transactional(readOnly = true)
361388
@Retention(RetentionPolicy.RUNTIME)
362389
@interface Meta2 {
363390
}
@@ -395,6 +422,28 @@ static interface InterfaceWithMetaAnnotation {
395422
static class ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface implements InterfaceWithMetaAnnotation {
396423
}
397424

425+
@Meta1
426+
static class ClassWithInheritedMetaAnnotation {
427+
}
428+
429+
@Meta2
430+
static class SubClassWithInheritedMetaAnnotation extends ClassWithInheritedMetaAnnotation {
431+
}
432+
433+
static class SubSubClassWithInheritedMetaAnnotation extends SubClassWithInheritedMetaAnnotation {
434+
}
435+
436+
@Transactional
437+
static class ClassWithInheritedAnnotation {
438+
}
439+
440+
@Meta2
441+
static class SubClassWithInheritedAnnotation extends ClassWithInheritedAnnotation {
442+
}
443+
444+
static class SubSubClassWithInheritedAnnotation extends SubClassWithInheritedAnnotation {
445+
}
446+
398447
@MetaMeta
399448
static class MetaMetaAnnotatedClass {
400449
}
@@ -453,6 +502,8 @@ public void overrideWithoutNewAnnotation() {
453502
@Retention(RetentionPolicy.RUNTIME)
454503
@Inherited
455504
@interface Transactional {
505+
506+
boolean readOnly() default false;
456507
}
457508

458509
public static abstract class Foo<T> {

0 commit comments

Comments
 (0)