Skip to content

Commit 1374d6d

Browse files
committed
Merge branch '5.3.x'
2 parents efb83fa + 622fc3e commit 1374d6d

File tree

2 files changed

+75
-8
lines changed

2 files changed

+75
-8
lines changed

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,11 @@ private <T extends Map<String, Object>> Object adaptValueForMapOptions(Method at
323323
@SuppressWarnings("unchecked")
324324
protected A createSynthesizedAnnotation() {
325325
// Check root annotation
326-
if (isTargetAnnotation(this.rootAttributes) && isNotSynthesizable((Annotation) this.rootAttributes)) {
326+
if (isTargetAnnotation(this.rootAttributes) && !isSynthesizable((Annotation) this.rootAttributes)) {
327327
return (A) this.rootAttributes;
328328
}
329329
// Check meta-annotation
330-
else if (isTargetAnnotation(this.mapping.getAnnotation()) && isNotSynthesizable(this.mapping.getAnnotation())) {
330+
else if (isTargetAnnotation(this.mapping.getAnnotation()) && !isSynthesizable(this.mapping.getAnnotation())) {
331331
return (A) this.mapping.getAnnotation();
332332
}
333333
return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getType());
@@ -344,14 +344,25 @@ private boolean isTargetAnnotation(@Nullable Object obj) {
344344
}
345345

346346
/**
347-
* Determine if the supplied annotation has already been synthesized or if the
348-
* mapped annotation is not {@linkplain AnnotationTypeMapping#isSynthesizable()
349-
* synthesizable} in general.
347+
* Determine if the supplied annotation has not already been synthesized
348+
* <strong>and</strong> whether the mapped annotation is a composed annotation
349+
* that needs to have its attributes merged or the mapped annotation is
350+
* {@linkplain AnnotationTypeMapping#isSynthesizable() synthesizable} in general.
350351
* @param annotation the annotation to check
351352
* @since 5.3.22
352353
*/
353-
private boolean isNotSynthesizable(Annotation annotation) {
354-
return (annotation instanceof SynthesizedAnnotation || !this.mapping.isSynthesizable());
354+
private boolean isSynthesizable(Annotation annotation) {
355+
// Already synthesized?
356+
if (annotation instanceof SynthesizedAnnotation) {
357+
return false;
358+
}
359+
// Is this a mapped annotation for a composed annotation, and are there
360+
// annotation attributes (mirrors) that need to be merged?
361+
if (getDistance() > 0 && this.resolvedMirrors.length > 0) {
362+
return true;
363+
}
364+
// Is the mapped annotation itself synthesizable?
365+
return this.mapping.isSynthesizable();
355366
}
356367

357368
@Override

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

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.lang.annotation.RetentionPolicy;
2525
import java.lang.annotation.Target;
2626
import java.lang.reflect.AnnotatedElement;
27+
import java.lang.reflect.Field;
2728
import java.lang.reflect.Method;
2829
import java.util.ArrayList;
2930
import java.util.Arrays;
@@ -1539,7 +1540,28 @@ void synthesizeShouldNotSynthesizeNonsynthesizableAnnotations() throws Exception
15391540
assertThat(generatedValue).isSameAs(synthesizedGeneratedValue);
15401541
}
15411542

1542-
@Test
1543+
@Test // gh-28716
1544+
void synthesizeWhenUsingMergedAnnotationsFromApi() {
1545+
Field directlyAnnotatedField = ReflectionUtils.findField(DomainType.class, "directlyAnnotated");
1546+
MergedAnnotations mergedAnnotations = MergedAnnotations.from(directlyAnnotatedField);
1547+
RootAnnotation rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
1548+
assertThat(rootAnnotation.flag()).isFalse();
1549+
assertThat(rootAnnotation).isNotInstanceOf(SynthesizedAnnotation.class);
1550+
1551+
Field metaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaAnnotated");
1552+
mergedAnnotations = MergedAnnotations.from(metaAnnotatedField);
1553+
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
1554+
assertThat(rootAnnotation.flag()).isTrue();
1555+
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
1556+
1557+
Field metaMetaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaMetaAnnotated");
1558+
mergedAnnotations = MergedAnnotations.from(metaMetaAnnotatedField);
1559+
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
1560+
assertThat(rootAnnotation.flag()).isTrue();
1561+
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
1562+
}
1563+
1564+
@Test // gh-28704
15431565
void synthesizeShouldNotSynthesizeNonsynthesizableAnnotationsWhenUsingMergedAnnotationsFromApi() {
15441566
MergedAnnotations mergedAnnotations = MergedAnnotations.from(SecurityConfig.class);
15451567

@@ -3246,6 +3268,40 @@ private Long getId() {
32463268
static class SecurityConfig {
32473269
}
32483270

3271+
@Retention(RetentionPolicy.RUNTIME)
3272+
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
3273+
@interface RootAnnotation {
3274+
String value() default "";
3275+
boolean flag() default false;
3276+
}
3277+
3278+
@RootAnnotation
3279+
@Retention(RetentionPolicy.RUNTIME)
3280+
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
3281+
@interface ComposedRootAnnotation {
3282+
3283+
@AliasFor(annotation = RootAnnotation.class, attribute = "flag")
3284+
boolean enabled() default true;
3285+
}
3286+
3287+
@Retention(RetentionPolicy.RUNTIME)
3288+
@Target(ElementType.FIELD)
3289+
@ComposedRootAnnotation
3290+
@interface DoublyComposedRootAnnotation {
3291+
}
3292+
3293+
class DomainType {
3294+
3295+
@RootAnnotation
3296+
Object directlyAnnotated;
3297+
3298+
@ComposedRootAnnotation
3299+
Object metaAnnotated;
3300+
3301+
@DoublyComposedRootAnnotation
3302+
Object metaMetaAnnotated;
3303+
}
3304+
32493305
@Retention(RetentionPolicy.RUNTIME)
32503306
@interface TestConfiguration {
32513307

0 commit comments

Comments
 (0)