-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Regression: default values for implicit aliases no longer honored in MergedAnnotations #24110
Conversation
… when some low level properties is mirrored and is default value, the high level properties will not changed, i think its unreasonable.
@iiiron Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
@iiiron Thank you for signing the Contributor License Agreement! |
@iiiron, thanks for submitting the PR. Do you have a use case that used to work but now fails with Spring Framework 5.2.x? |
No I didn't,I just find it by reading the source code.
|
Thanks for providing feedback. Since the proposed change does not address a regression, I would like to wait on input from @philwebb. In addition, after reviewing the affected code, it appears to me that leaving the |
I'm not sure I totally understand the issue. @iiiron can you clarify what you mean by "when some low-level attribute is mirrored and is the default value, the high-level attribute will not be changed"? Perhaps you can provide some sample annotations that show what you mean? |
think that.
```java
@TestB
@retention(RetentionPolicy.RUNTIME)
@target({ElementType.TYPE})
public @interface TestA {
@AliasFor(value = "b1", annotation = TestB.class)
String a1() default "testA";
}
@testc
@retention(RetentionPolicy.RUNTIME)
@target({ElementType.TYPE})
public @interface TestB {
@AliasFor(value = "b2")
String b1() default "testB";
@AliasFor(value = "b1")
String b2() default "testB";
}
@testa
public class Test {
public static void main(String[] args) { // you will get an AnnotationAttributes what key b1 set to "testA" and key b2 set to "testA"
Object b = AnnotatedElementUtils.findMergedAnnotationAttributes(Test.class, TestB.class, false, true);
}
}
```
but when you make `TestA` like that
```java
@TestB
@retention(RetentionPolicy.RUNTIME)
@target({ElementType.TYPE})
public @interface TestA {
@AliasFor(value = "b1", annotation = TestB.class)
String a1() default "testA";
@AliasFor(value = "b2", annotation = TestB.class)
String a2() default "testA";
}
@testa
public class Test {
public static void main(String[] args) { // you will get an AnnotationAttributes what key b1 set to "testB" and key b2 set to "testB"
Object b = AnnotatedElementUtils.findMergedAnnotationAttributes(Test.class, TestB.class, false, true);
}
}
```
I think it's unreasonable, when the attributes in `TestA` become to mirrors, it will not effect `TestB`. The low-level annotation attributes should have priority over high-level annotation attributes, like subclass can overwrite the methods in super-class.
|
Thanks for providing the example. In terms of executable tests, the following passes against class TestCase {
@Test
void findMergedAnnotation() {
MyAnnotation myAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedClass.class,
MyAnnotation.class);
assertThat(myAnnotation).isNotNull();
assertThat(myAnnotation.b1()).isEqualTo("testA");
assertThat(myAnnotation.b2()).isEqualTo("testA");
}
@Test
void findMergedAnnotationAttributes() {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(AnnotatedClass.class,
MyAnnotation.class, false, true);
assertThat(attributes).isNotNull();
assertThat(attributes).size().isEqualTo(2);
assertThat(attributes.get("b1")).isEqualTo("testA");
assertThat(attributes.get("b2")).isEqualTo("testA");
}
} @Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
@AliasFor("b2")
String b1() default "testB";
@AliasFor("b1")
String b2() default "testB";
} @MyAnnotation
@Retention(RetentionPolicy.RUNTIME)
@interface ComposedAnnotation {
@AliasFor(attribute = "b1", annotation = MyAnnotation.class)
String a1() default "testA";
} @ComposedAnnotation
class AnnotatedClass {
} |
In addition, the following JUnit 4 based tests pass against public class TestCase {
@Test
public void findMergedAnnotation() {
MyAnnotation myAnnotation = AnnotatedElementUtils.findMergedAnnotation(AnnotatedClass.class,
MyAnnotation.class);
assertNotNull(myAnnotation);
assertEquals("testA", myAnnotation.b1());
assertEquals("testA", myAnnotation.b2());
}
@Test
public void findMergedAnnotationAttributes() {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(AnnotatedClass.class,
MyAnnotation.class, false, true);
assertNotNull(attributes);
assertEquals(2, attributes.size());
assertEquals("testA", attributes.get("b1"));
assertEquals("testA", attributes.get("b2"));
}
} @Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
@AliasFor("b2")
String b1() default "testB";
@AliasFor("b1")
String b2() default "testB";
} @MyAnnotation
@Retention(RetentionPolicy.RUNTIME)
@interface ComposedAnnotation {
@AliasFor(attribute = "b1", annotation = MyAnnotation.class)
String a1() default "testA";
} @ComposedAnnotation
class AnnotatedClass {
} Thus, there is not a regression in |
That is exactly the behavior that is supported. In the tests I provided, In light of that, I am closing this PR as "works as designed". For further information on Spring's support for composed annotations and |
thank you for your feedback!
but do you test that case:
```java
@MyAnnotation @retention(RetentionPolicy.RUNTIME) @interface ComposedAnnotation { @AliasFor(attribute = "b1", annotation = MyAnnotation.class) String a1() default "testA"; @AliasFor(attribute = "b2", annotation = MyAnnotation.class) String a2() default "testA";}
```
|
The tests still pass against @ComposedAnnotation2
class AnnotatedClass {
}
@MyAnnotation
@Retention(RetentionPolicy.RUNTIME)
@interface ComposedAnnotation2 {
@AliasFor(attribute = "b1", annotation = MyAnnotation.class)
String a1() default "testA";
@AliasFor(attribute = "b2", annotation = MyAnnotation.class)
String a2() default "testA";
} However, the tests in fact fail against Thus I am reopening this issue since it is in fact a regression. @iiiron, thank you very much bringing this to our attention! |
thank you, it's my pleasure!
|
The proposed code change in the PR appears to fix the regression. So I've scheduled this for 5.2.3. |
…mework into 5.1.x * '5.1.x' of https://github.com/spring-projects/spring-framework: (26 commits) Add integration test for spring-projectsgh-24110 Next Development Version Provide default codecs config callback to custom codecs Allow ExchangeStrategies customizations in WebClient Upgrade to AspectJ 1.9.5 and Checkstyle 8.27 Revert "Allow ExchangeStrategies customizations in WebClient" Polishing Polishing Backport of recent ExtendedBeanInfo refinements from master Allow ExchangeStrategies customizations in WebClient Test status quo for @inherited annotations in AnnotationMetadata Test status quo for AnnotatedTypeMetadata.getAnnotationAttributes() Upgrade to Reactor Californium-SR14 Remove println Fix NullPointerException in Jackson2SmileDecoder Upgrade to Tomcat 9.0.29, Jetty 9.4.24, RxJava 2.2.15 Add missing verify() in Jackson2TokenizerTests Extra isReady-onWritePossible after last write Restore short-circuiting in equals implementation Upgrade to Jetty 9.4.23 and Woodstox 5.3 ...
…amework into read_yuyang * 'master' of https://github.com/spring-projects/spring-framework: (32 commits) Polishing Add missing backtick in WebSocket documentation Hoist Class.getName() from String concatenation to dodge an issue related to profile pollution Support variable resolution of wildcard types Test status quo for @inherited annotation support in AnnotationMetadata Polishing Add @SInCE tags to firstElement methods Add firstElement to CollectionUtils Fix status code in webflux.adoc Consistently use releaseBody in DefaultWebClient Replace ReadCancellationException with takeWhile Add UriUtils.encodeQueryParams Remove mismatched marker in core-beans.adoc Add integration test for spring-projectsgh-24110 Honor default values for implicit aliases in composed annotations Next Development Version Polish Provide default codecs config callback to custom codecs [*.*] is displayed as [bold .] and needs to be escaped Polishing (follow-up on acfeb7) ...
A single low-level annotation attribute can change a high-level attribute via
@AliasFor
, but when some low-level attribute is mirrored and is the default value, the high-level attribute will not be changed.I think this is unreasonable.