Skip to content

Commit 6a68bd5

Browse files
committed
Introduce AnnotationUtils.isSynthesizedAnnotation(Annotation)
Since SynthesizedAnnotation will be deprecated (and potentially completely removed) in Spring Framework 6.0, this commit introduces AnnotationUtils.isSynthesizedAnnotation(Annotation) in 5.3.x to allow people to migrate away from relying on SynthesizedAnnotation. Closes gh-29054
1 parent b1414bf commit 6a68bd5

File tree

6 files changed

+59
-41
lines changed

6 files changed

+59
-41
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -173,7 +173,7 @@
173173
* @author Sam Brannen
174174
* @since 4.2
175175
* @see MergedAnnotations
176-
* @see SynthesizedAnnotation
176+
* @see AnnotationUtils#isSynthesizedAnnotation(Annotation)
177177
*/
178178
@Retention(RetentionPolicy.RUNTIME)
179179
@Target(ElementType.METHOD)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
2525
* @author Sam Brannen
2626
* @since 4.2
2727
* @see AnnotationUtils
28-
* @see SynthesizedAnnotation
28+
* @see AnnotationUtils#isSynthesizedAnnotation(java.lang.annotation.Annotation)
2929
*/
3030
@SuppressWarnings("serial")
3131
public class AnnotationConfigurationException extends NestedRuntimeException {

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1188,7 +1188,7 @@ public static Object getDefaultValue(
11881188
public static <A extends Annotation> A synthesizeAnnotation(
11891189
A annotation, @Nullable AnnotatedElement annotatedElement) {
11901190

1191-
if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) {
1191+
if (isSynthesizedAnnotation(annotation) || AnnotationFilter.PLAIN.matches(annotation)) {
11921192
return annotation;
11931193
}
11941194
return MergedAnnotation.from(annotatedElement, annotation).synthesize();
@@ -1282,6 +1282,18 @@ static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Annotate
12821282
return synthesized;
12831283
}
12841284

1285+
/**
1286+
* Determine if the supplied {@link Annotation} has been <em>synthesized</em>
1287+
* by Spring (i.e. wrapped in a dynamic proxy) with additional functionality
1288+
* such as attribute alias handling.
1289+
* @param annotation the annotation to check
1290+
* @return {@code true} if the supplied annotation is a synthesized annotation
1291+
* @since 5.3.23
1292+
*/
1293+
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
1294+
return (annotation instanceof SynthesizedAnnotation);
1295+
}
1296+
12851297
/**
12861298
* Clear the internal annotation metadata cache.
12871299
* @since 4.3.15

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void multiplRoutesToMetaAnnotation() {
4545
@Test
4646
void defaultValue() {
4747
DefaultValueAnnotation synthesized = MergedAnnotations.from(WithDefaultValue.class).get(DefaultValueAnnotation.class).synthesize();
48-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
48+
assertThat(AnnotationUtils.isSynthesizedAnnotation(synthesized)).as("synthesized annotation").isTrue();
4949
Object defaultValue = AnnotationUtils.getDefaultValue(synthesized, "enumValue");
5050
assertThat(defaultValue).isEqualTo(TestEnum.ONE);
5151
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ void toAnnotationSetCollectsLinkedHashSetWithSynthesizedAnnotations() {
4545
MergedAnnotationCollectors.toAnnotationSet());
4646
assertThat(set).isInstanceOf(LinkedHashSet.class).flatExtracting(
4747
TestAnnotation::value).containsExactly("a", "b", "c");
48-
assertThat(set).allMatch(SynthesizedAnnotation.class::isInstance);
48+
assertThat(set).allMatch(AnnotationUtils::isSynthesizedAnnotation);
4949
}
5050

5151
@Test
@@ -55,7 +55,7 @@ void toAnnotationArrayCollectsAnnotationArrayWithSynthesizedAnnotations() {
5555
assertThat(Arrays.stream(array).map(
5656
annotation -> ((TestAnnotation) annotation).value())).containsExactly("a",
5757
"b", "c");
58-
assertThat(array).allMatch(SynthesizedAnnotation.class::isInstance);
58+
assertThat(array).allMatch(AnnotationUtils::isSynthesizedAnnotation);
5959
}
6060

6161
@Test
@@ -64,7 +64,7 @@ void toSuppliedAnnotationArrayCollectsAnnotationArrayWithSynthesizedAnnotations(
6464
MergedAnnotationCollectors.toAnnotationArray(TestAnnotation[]::new));
6565
assertThat(Arrays.stream(array).map(TestAnnotation::value)).containsExactly("a",
6666
"b", "c");
67-
assertThat(array).allMatch(SynthesizedAnnotation.class::isInstance);
67+
assertThat(array).allMatch(AnnotationUtils::isSynthesizedAnnotation);
6868
}
6969

7070
@Test

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

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,9 +1395,10 @@ void synthesizeAlreadySynthesized() throws Exception {
13951395
RequestMapping synthesizedWebMapping = MergedAnnotation.from(webMapping).synthesize();
13961396
RequestMapping synthesizedAgainWebMapping = MergedAnnotation.from(synthesizedWebMapping).synthesize();
13971397

1398-
assertThat(synthesizedWebMapping).isInstanceOf(SynthesizedAnnotation.class);
1399-
assertThat(synthesizedAgainWebMapping).isInstanceOf(SynthesizedAnnotation.class);
1398+
assertSynthesized(synthesizedWebMapping);
1399+
assertSynthesized(synthesizedAgainWebMapping);
14001400
assertThat(synthesizedWebMapping).isEqualTo(synthesizedAgainWebMapping);
1401+
assertThat(synthesizedWebMapping).isSameAs(synthesizedAgainWebMapping);
14011402
assertThat(synthesizedWebMapping.name()).isEqualTo("foo");
14021403
assertThat(synthesizedWebMapping.path()).containsExactly("/test");
14031404
assertThat(synthesizedWebMapping.value()).containsExactly("/test");
@@ -1412,15 +1413,15 @@ void synthesizeShouldNotSynthesizeNonsynthesizableAnnotations() throws Exception
14121413
Id synthesizedId = MergedAnnotation.from(id).synthesize();
14131414
assertThat(id).isEqualTo(synthesizedId);
14141415
// It doesn't make sense to synthesize @Id since it declares zero attributes.
1415-
assertThat(synthesizedId).isNotInstanceOf(SynthesizedAnnotation.class);
1416+
assertNotSynthesized(synthesizedId);
14161417
assertThat(id).isSameAs(synthesizedId);
14171418

14181419
GeneratedValue generatedValue = method.getAnnotation(GeneratedValue.class);
14191420
assertThat(generatedValue).isNotNull();
14201421
GeneratedValue synthesizedGeneratedValue = MergedAnnotation.from(generatedValue).synthesize();
14211422
assertThat(generatedValue).isEqualTo(synthesizedGeneratedValue);
14221423
// It doesn't make sense to synthesize @GeneratedValue since it declares zero attributes with aliases.
1423-
assertThat(synthesizedGeneratedValue).isNotInstanceOf(SynthesizedAnnotation.class);
1424+
assertNotSynthesized(synthesizedGeneratedValue);
14241425
assertThat(generatedValue).isSameAs(synthesizedGeneratedValue);
14251426
}
14261427

@@ -1430,30 +1431,30 @@ void synthesizeWhenUsingMergedAnnotationsFromApi() {
14301431
MergedAnnotations mergedAnnotations = MergedAnnotations.from(directlyAnnotatedField);
14311432
RootAnnotation rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
14321433
assertThat(rootAnnotation.flag()).isFalse();
1433-
assertThat(rootAnnotation).isNotInstanceOf(SynthesizedAnnotation.class);
1434+
assertNotSynthesized(rootAnnotation);
14341435

14351436
Field metaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaAnnotated");
14361437
mergedAnnotations = MergedAnnotations.from(metaAnnotatedField);
14371438
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
14381439
assertThat(rootAnnotation.flag()).isTrue();
1439-
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
1440+
assertSynthesized(rootAnnotation);
14401441

14411442
Field metaMetaAnnotatedField = ReflectionUtils.findField(DomainType.class, "metaMetaAnnotated");
14421443
mergedAnnotations = MergedAnnotations.from(metaMetaAnnotatedField);
14431444
rootAnnotation = mergedAnnotations.get(RootAnnotation.class).synthesize();
14441445
assertThat(rootAnnotation.flag()).isTrue();
1445-
assertThat(rootAnnotation).isInstanceOf(SynthesizedAnnotation.class);
1446+
assertSynthesized(rootAnnotation);
14461447
}
14471448

14481449
@Test // gh-28704
14491450
void synthesizeShouldNotSynthesizeNonsynthesizableAnnotationsWhenUsingMergedAnnotationsFromApi() {
14501451
MergedAnnotations mergedAnnotations = MergedAnnotations.from(SecurityConfig.class);
14511452

14521453
EnableWebSecurity enableWebSecurity = mergedAnnotations.get(EnableWebSecurity.class).synthesize();
1453-
assertThat(enableWebSecurity).isNotInstanceOf(SynthesizedAnnotation.class);
1454+
assertNotSynthesized(enableWebSecurity);
14541455

14551456
EnableGlobalAuthentication enableGlobalAuthentication = mergedAnnotations.get(EnableGlobalAuthentication.class).synthesize();
1456-
assertThat(enableGlobalAuthentication).isNotInstanceOf(SynthesizedAnnotation.class);
1457+
assertNotSynthesized(enableGlobalAuthentication);
14571458
}
14581459

14591460
/**
@@ -1472,8 +1473,8 @@ void synthesizeShouldNotResynthesizeAlreadySynthesizedAnnotations() throws Excep
14721473
RequestMapping synthesizedWebMapping1 = mergedAnnotation1.synthesize();
14731474
RequestMapping synthesizedWebMapping2 = MergedAnnotation.from(webMapping).synthesize();
14741475

1475-
assertThat(synthesizedWebMapping1).isInstanceOf(SynthesizedAnnotation.class);
1476-
assertThat(synthesizedWebMapping2).isInstanceOf(SynthesizedAnnotation.class);
1476+
assertSynthesized(synthesizedWebMapping1);
1477+
assertSynthesized(synthesizedWebMapping2);
14771478
assertThat(synthesizedWebMapping1).isEqualTo(synthesizedWebMapping2);
14781479

14791480
// Synthesizing an annotation from a different MergedAnnotation results in a different synthesized annotation instance.
@@ -1595,14 +1596,11 @@ void synthesizeWithImplicitAliases() throws Exception {
15951596
testSynthesisWithImplicitAliases(GroovyImplicitAliasesSimpleTestConfigurationClass.class, "groovyScript");
15961597
}
15971598

1598-
private void testSynthesisWithImplicitAliases(Class<?> clazz, String expected)
1599-
throws Exception {
1600-
ImplicitAliasesTestConfiguration config = clazz.getAnnotation(
1601-
ImplicitAliasesTestConfiguration.class);
1599+
private void testSynthesisWithImplicitAliases(Class<?> clazz, String expected) throws Exception {
1600+
ImplicitAliasesTestConfiguration config = clazz.getAnnotation(ImplicitAliasesTestConfiguration.class);
16021601
assertThat(config).isNotNull();
1603-
ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(
1604-
config).synthesize();
1605-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1602+
ImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
1603+
assertSynthesized(synthesized);
16061604
assertThat(synthesized.value()).isEqualTo(expected);
16071605
assertThat(synthesized.location1()).isEqualTo(expected);
16081606
assertThat(synthesized.xmlFile()).isEqualTo(expected);
@@ -1630,7 +1628,7 @@ private void testSynthesisWithImplicitAliasesWithImpliedAliasNamesOmitted(
16301628
assertThat(config).isNotNull();
16311629
ImplicitAliasesWithImpliedAliasNamesOmittedTestConfiguration synthesized =
16321630
MergedAnnotation.from(config).synthesize();
1633-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1631+
assertSynthesized(synthesized);
16341632
assertThat(synthesized.value()).isEqualTo(expected);
16351633
assertThat(synthesized.location()).isEqualTo(expected);
16361634
assertThat(synthesized.xmlFile()).isEqualTo(expected);
@@ -1642,7 +1640,7 @@ void synthesizeWithImplicitAliasesForAliasPair() throws Exception {
16421640
ImplicitAliasesForAliasPairTestConfigurationClass.class.getAnnotation(
16431641
ImplicitAliasesForAliasPairTestConfiguration.class);
16441642
ImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
1645-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1643+
assertSynthesized(synthesized);
16461644
assertThat(synthesized.xmlFile()).isEqualTo("test.xml");
16471645
assertThat(synthesized.groovyScript()).isEqualTo("test.xml");
16481646
}
@@ -1653,7 +1651,7 @@ void synthesizeWithTransitiveImplicitAliases() throws Exception {
16531651
TransitiveImplicitAliasesTestConfigurationClass.class.getAnnotation(
16541652
TransitiveImplicitAliasesTestConfiguration.class);
16551653
TransitiveImplicitAliasesTestConfiguration synthesized = MergedAnnotation.from(config).synthesize();
1656-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1654+
assertSynthesized(synthesized);
16571655
assertThat(synthesized.xml()).isEqualTo("test.xml");
16581656
assertThat(synthesized.groovy()).isEqualTo("test.xml");
16591657
}
@@ -1665,7 +1663,7 @@ void synthesizeWithTransitiveImplicitAliasesForAliasPair() throws Exception {
16651663
TransitiveImplicitAliasesForAliasPairTestConfiguration.class);
16661664
TransitiveImplicitAliasesForAliasPairTestConfiguration synthesized = MergedAnnotation.from(
16671665
config).synthesize();
1668-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1666+
assertSynthesized(synthesized);
16691667
assertThat(synthesized.xml()).isEqualTo("test.xml");
16701668
assertThat(synthesized.groovy()).isEqualTo("test.xml");
16711669
}
@@ -1724,7 +1722,7 @@ void synthesizeFromMapWithoutAttributeAliases() throws Exception {
17241722
Map<String, Object> map = Collections.singletonMap("value", "webController");
17251723
MergedAnnotation<Component> annotation = MergedAnnotation.of(Component.class, map);
17261724
Component synthesizedComponent = annotation.synthesize();
1727-
assertThat(synthesizedComponent).isInstanceOf(SynthesizedAnnotation.class);
1725+
assertSynthesized(synthesizedComponent);
17281726
assertThat(synthesizedComponent.value()).isEqualTo("webController");
17291727
}
17301728

@@ -1745,7 +1743,7 @@ void synthesizeFromMapWithNestedMap() throws Exception {
17451743
MergedAnnotation<ComponentScanSingleFilter> annotation = MergedAnnotation.of(
17461744
ComponentScanSingleFilter.class, map);
17471745
ComponentScanSingleFilter synthesizedComponentScan = annotation.synthesize();
1748-
assertThat(synthesizedComponentScan).isInstanceOf(SynthesizedAnnotation.class);
1746+
assertSynthesized(synthesizedComponentScan);
17491747
assertThat(synthesizedComponentScan.value().pattern()).isEqualTo("newFoo");
17501748
}
17511749

@@ -1769,7 +1767,7 @@ void synthesizeFromMapWithNestedArrayOfMaps() throws Exception {
17691767
MergedAnnotation<ComponentScan> annotation = MergedAnnotation.of(
17701768
ComponentScan.class, map);
17711769
ComponentScan synthesizedComponentScan = annotation.synthesize();
1772-
assertThat(synthesizedComponentScan).isInstanceOf(SynthesizedAnnotation.class);
1770+
assertSynthesized(synthesizedComponentScan);
17731771
assertThat(Arrays.stream(synthesizedComponentScan.excludeFilters()).map(
17741772
Filter::pattern)).containsExactly("newFoo", "newBar");
17751773
}
@@ -1888,7 +1886,7 @@ void synthesizeFromAnnotationAttributesWithoutAttributeAliases() throws Exceptio
18881886
assertThat(component).isNotNull();
18891887
Map<String, Object> attributes = MergedAnnotation.from(component).asMap();
18901888
Component synthesized = MergedAnnotation.of(Component.class, attributes).synthesize();
1891-
assertThat(synthesized).isInstanceOf(SynthesizedAnnotation.class);
1889+
assertSynthesized(synthesized);
18921890
assertThat(synthesized).isEqualTo(component);
18931891
}
18941892

@@ -2047,7 +2045,7 @@ void synthesizeNonPublicWithAttributeAliasesFromDifferentPackage() throws Except
20472045
assertThat(annotation).isNotNull();
20482046
MergedAnnotation<Annotation> mergedAnnotation = MergedAnnotation.from(annotation);
20492047
Annotation synthesizedAnnotation = mergedAnnotation.synthesize();
2050-
assertThat(synthesizedAnnotation).isInstanceOf(SynthesizedAnnotation.class);
2048+
assertSynthesized(synthesizedAnnotation);
20512049
assertThat(mergedAnnotation.getString("name")).isEqualTo("test");
20522050
assertThat(mergedAnnotation.getString("path")).isEqualTo("/test");
20532051
assertThat(mergedAnnotation.getString("value")).isEqualTo("/test");
@@ -2058,10 +2056,10 @@ void synthesizeWithArrayOfAnnotations() throws Exception {
20582056
Hierarchy hierarchy = HierarchyClass.class.getAnnotation(Hierarchy.class);
20592057
assertThat(hierarchy).isNotNull();
20602058
Hierarchy synthesizedHierarchy = MergedAnnotation.from(hierarchy).synthesize();
2061-
assertThat(synthesizedHierarchy).isInstanceOf(SynthesizedAnnotation.class);
2059+
assertSynthesized(synthesizedHierarchy);
20622060
TestConfiguration[] configs = synthesizedHierarchy.value();
20632061
assertThat(configs).isNotNull();
2064-
assertThat(configs).allMatch(SynthesizedAnnotation.class::isInstance);
2062+
assertThat(configs).allMatch(AnnotationUtils::isSynthesizedAnnotation);
20652063
assertThat(configs).extracting(TestConfiguration::value).containsExactly("A", "B");
20662064
assertThat(configs).extracting(TestConfiguration::location).containsExactly("A", "B");
20672065

@@ -2082,7 +2080,7 @@ void synthesizeWithArrayOfChars() throws Exception {
20822080
assertThat(charsContainer).isNotNull();
20832081
CharsContainer synthesizedCharsContainer = MergedAnnotation.from(
20842082
charsContainer).synthesize();
2085-
assertThat(synthesizedCharsContainer).isInstanceOf(SynthesizedAnnotation.class);
2083+
assertSynthesized(synthesizedCharsContainer);
20862084
char[] chars = synthesizedCharsContainer.chars();
20872085
assertThat(chars).containsExactly('x', 'y', 'z');
20882086
// Alter array returned from synthesized annotation
@@ -3682,4 +3680,12 @@ static class ValueAttributeMetaMetaClass {
36823680
}
36833681
// @formatter:on
36843682

3683+
static void assertSynthesized(Annotation annotation) {
3684+
assertThat(AnnotationUtils.isSynthesizedAnnotation(annotation)).as("synthesized annotation").isTrue();
3685+
}
3686+
3687+
static void assertNotSynthesized(Annotation annotation) {
3688+
assertThat(AnnotationUtils.isSynthesizedAnnotation(annotation)).as("synthesized annotation").isFalse();
3689+
}
3690+
36853691
}

0 commit comments

Comments
 (0)