Skip to content

Commit d081470

Browse files
committed
AnnotationUtils.synthesizeAnnotation explicitly checks whether SynthesizedAnnotation is exposable
Issue: SPR-13696
1 parent 40cff5e commit d081470

File tree

2 files changed

+63
-27
lines changed

2 files changed

+63
-27
lines changed

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ private static <A extends Annotation> A findAnnotation(AnnotatedElement annotate
514514
try {
515515
Annotation[] anns = annotatedElement.getDeclaredAnnotations();
516516
for (Annotation ann : anns) {
517-
if (ann.annotationType().equals(annotationType)) {
517+
if (ann.annotationType() == annotationType) {
518518
return (A) ann;
519519
}
520520
}
@@ -703,7 +703,7 @@ private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A>
703703
try {
704704
Annotation[] anns = clazz.getDeclaredAnnotations();
705705
for (Annotation ann : anns) {
706-
if (ann.annotationType().equals(annotationType)) {
706+
if (ann.annotationType() == annotationType) {
707707
return (A) ann;
708708
}
709709
}
@@ -828,7 +828,7 @@ public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> an
828828
Assert.notNull(clazz, "Class must not be null");
829829
try {
830830
for (Annotation ann : clazz.getDeclaredAnnotations()) {
831-
if (ann.annotationType().equals(annotationType)) {
831+
if (ann.annotationType() == annotationType) {
832832
return true;
833833
}
834834
}
@@ -1360,7 +1360,8 @@ public static <A extends Annotation> A synthesizeAnnotation(A annotation, Annota
13601360
if (annotation == null) {
13611361
return null;
13621362
}
1363-
if (annotation instanceof SynthesizedAnnotation) {
1363+
if (annotation instanceof SynthesizedAnnotation || (Proxy.isProxyClass(annotation.getClass()) &&
1364+
Proxy.getInvocationHandler(annotation) instanceof SynthesizedAnnotationInvocationHandler)) {
13641365
return annotation;
13651366
}
13661367

@@ -1372,8 +1373,9 @@ public static <A extends Annotation> A synthesizeAnnotation(A annotation, Annota
13721373
DefaultAnnotationAttributeExtractor attributeExtractor =
13731374
new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
13741375
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1375-
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(),
1376-
new Class<?>[] {(Class<A>) annotationType, SynthesizedAnnotation.class}, handler);
1376+
Class<?>[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ?
1377+
new Class<?>[] {annotationType, SynthesizedAnnotation.class} : new Class<?>[] {annotationType});
1378+
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
13771379
}
13781380

13791381
/**
@@ -1418,8 +1420,9 @@ public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object>
14181420
MapAnnotationAttributeExtractor attributeExtractor =
14191421
new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement);
14201422
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
1421-
return (A) Proxy.newProxyInstance(annotationType.getClassLoader(),
1422-
new Class<?>[] {annotationType, SynthesizedAnnotation.class}, handler);
1423+
Class<?>[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ?
1424+
new Class<?>[] {annotationType, SynthesizedAnnotation.class} : new Class<?>[] {annotationType});
1425+
return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler);
14231426
}
14241427

14251428
/**
@@ -1539,10 +1542,23 @@ static Map<String, List<String>> getAttributeAliasMap(Class<? extends Annotation
15391542
}
15401543

15411544
attributeAliasesCache.put(annotationType, map);
1542-
15431545
return map;
15441546
}
15451547

1548+
/**
1549+
* Check whether we can expose our {@link SynthesizedAnnotation} marker for the given annotation type.
1550+
* @param annotationType the annotation type that we are about to create a synthesized proxy for
1551+
*/
1552+
private static boolean canExposeSynthesizedMarker(Class<? extends Annotation> annotationType) {
1553+
try {
1554+
return (Class.forName(SynthesizedAnnotation.class.getName(), false, annotationType.getClassLoader()) ==
1555+
SynthesizedAnnotation.class);
1556+
}
1557+
catch (ClassNotFoundException ex) {
1558+
return false;
1559+
}
1560+
}
1561+
15461562
/**
15471563
* Determine if annotations of the supplied {@code annotationType} are
15481564
* <em>synthesizable</em> (i.e., in need of being wrapped in a dynamic
@@ -1629,7 +1645,7 @@ static List<String> getAttributeAliasNames(Method attribute) {
16291645
static String getAttributeOverrideName(Method attribute, Class<? extends Annotation> metaAnnotationType) {
16301646
Assert.notNull(attribute, "attribute must not be null");
16311647
Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
1632-
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
1648+
Assert.isTrue(Annotation.class != metaAnnotationType,
16331649
"metaAnnotationType must not be [java.lang.annotation.Annotation]");
16341650

16351651
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
@@ -1931,7 +1947,7 @@ private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
19311947
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
19321948
this.sourceAttributeName = this.sourceAttribute.getName();
19331949

1934-
this.aliasedAnnotationType = (Annotation.class.equals(aliasFor.annotation()) ?
1950+
this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ?
19351951
this.sourceAnnotationType : aliasFor.annotation());
19361952
this.aliasedAttributeName = getAliasedAttributeName(aliasFor, this.sourceAttribute);
19371953
try {
@@ -1945,7 +1961,7 @@ private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
19451961
throw new AnnotationConfigurationException(msg, ex);
19461962
}
19471963

1948-
this.isAliasPair = this.sourceAnnotationType.equals(this.aliasedAnnotationType);
1964+
this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType);
19491965
}
19501966

19511967
private void validate() {
@@ -1979,7 +1995,7 @@ private void validate() {
19791995

19801996
Class<?> returnType = this.sourceAttribute.getReturnType();
19811997
Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType();
1982-
if (!returnType.equals(aliasedReturnType)) {
1998+
if (returnType != aliasedReturnType) {
19831999
String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] " +
19842000
"and attribute [%s] in annotation [%s] must declare the same return type.",
19852001
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
@@ -2030,7 +2046,7 @@ private void validateAgainst(AliasDescriptor otherDescriptor) {
20302046
* @see #isAliasFor
20312047
*/
20322048
private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) {
2033-
return this.aliasedAnnotationType.equals(metaAnnotationType);
2049+
return (this.aliasedAnnotationType == metaAnnotationType);
20342050
}
20352051

20362052
/**
@@ -2087,7 +2103,7 @@ private List<AliasDescriptor> getOtherDescriptors() {
20872103

20882104
public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) {
20892105
Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null");
2090-
Assert.isTrue(!Annotation.class.equals(metaAnnotationType),
2106+
Assert.isTrue(Annotation.class != metaAnnotationType,
20912107
"metaAnnotationType must not be [java.lang.annotation.Annotation]");
20922108

20932109
// Search the attribute override hierarchy, starting with the current attribute

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

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
import java.lang.annotation.RetentionPolicy;
2424
import java.lang.annotation.Target;
2525
import java.lang.reflect.AnnotatedElement;
26+
import java.lang.reflect.Constructor;
2627
import java.lang.reflect.Method;
28+
import java.util.Date;
2729
import java.util.List;
2830
import java.util.Set;
31+
import javax.annotation.Resource;
2932

3033
import org.junit.Ignore;
3134
import org.junit.Rule;
@@ -632,7 +635,20 @@ public void findMergedAnnotationWithLocalAliasesThatConflictWithAttributesInMeta
632635
assertArrayEquals("locations for " + element, EMPTY, contextConfig.locations());
633636
// 'value' in @SpringAppConfig should not override 'value' in @ContextConfig
634637
assertArrayEquals("value for " + element, EMPTY, contextConfig.value());
635-
assertArrayEquals("classes for " + element, new Class<?>[] { Number.class }, contextConfig.classes());
638+
assertArrayEquals("classes for " + element, new Class<?>[] {Number.class}, contextConfig.classes());
639+
}
640+
641+
@Test
642+
public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception {
643+
Constructor<?> deprecatedCtor = Date.class.getConstructor(String.class);
644+
assertEquals(deprecatedCtor.getAnnotation(Deprecated.class), findMergedAnnotation(deprecatedCtor, Deprecated.class));
645+
assertEquals(Date.class.getAnnotation(Deprecated.class), findMergedAnnotation(Date.class, Deprecated.class));
646+
}
647+
648+
@Test
649+
public void javaxAnnotationTypeViaFindMergedAnnotation() throws Exception {
650+
assertEquals(ResourceHolder.class.getAnnotation(Resource.class), findMergedAnnotation(ResourceHolder.class, Resource.class));
651+
assertEquals(SpringAppConfigClass.class.getAnnotation(Resource.class), findMergedAnnotation(SpringAppConfigClass.class, Resource.class));
636652
}
637653

638654

@@ -685,7 +701,7 @@ static class MetaCycleAnnotatedClass {
685701
// -------------------------------------------------------------------------
686702

687703
@Retention(RetentionPolicy.RUNTIME)
688-
@Target({ ElementType.TYPE, ElementType.METHOD })
704+
@Target({ElementType.TYPE, ElementType.METHOD})
689705
@Inherited
690706
@interface Transactional {
691707

@@ -697,7 +713,7 @@ static class MetaCycleAnnotatedClass {
697713
}
698714

699715
@Retention(RetentionPolicy.RUNTIME)
700-
@Target({ ElementType.TYPE, ElementType.METHOD })
716+
@Target({ElementType.TYPE, ElementType.METHOD})
701717
@Inherited
702718
@interface AliasedTransactional {
703719

@@ -849,7 +865,7 @@ static class MetaCycleAnnotatedClass {
849865
String[] value() default {};
850866
}
851867

852-
@ImplicitAliasesContextConfig(xmlFiles = { "A.xml", "B.xml" })
868+
@ImplicitAliasesContextConfig(xmlFiles = {"A.xml", "B.xml"})
853869
@Retention(RetentionPolicy.RUNTIME)
854870
@interface ComposedImplicitAliasesContextConfig {
855871
}
@@ -938,7 +954,7 @@ static class MetaCycleAnnotatedClass {
938954
String pattern();
939955
}
940956

941-
@ComponentScan(excludeFilters = { @Filter(pattern = "*Test"), @Filter(pattern = "*Tests") })
957+
@ComponentScan(excludeFilters = {@Filter(pattern = "*Test"), @Filter(pattern = "*Tests")})
942958
@Retention(RetentionPolicy.RUNTIME)
943959
@interface TestComponentScan {
944960

@@ -1037,7 +1053,7 @@ public void handleFromInterface() {
10371053
}
10381054
}
10391055

1040-
public static interface GenericParameter<T> {
1056+
public interface GenericParameter<T> {
10411057

10421058
T getFor(Class<T> cls);
10431059
}
@@ -1057,23 +1073,23 @@ public String getFor(Integer integer) {
10571073
}
10581074

10591075
@Transactional
1060-
public static interface InheritedAnnotationInterface {
1076+
public interface InheritedAnnotationInterface {
10611077
}
10621078

1063-
public static interface SubInheritedAnnotationInterface extends InheritedAnnotationInterface {
1079+
public interface SubInheritedAnnotationInterface extends InheritedAnnotationInterface {
10641080
}
10651081

1066-
public static interface SubSubInheritedAnnotationInterface extends SubInheritedAnnotationInterface {
1082+
public interface SubSubInheritedAnnotationInterface extends SubInheritedAnnotationInterface {
10671083
}
10681084

10691085
@Order
1070-
public static interface NonInheritedAnnotationInterface {
1086+
public interface NonInheritedAnnotationInterface {
10711087
}
10721088

1073-
public static interface SubNonInheritedAnnotationInterface extends NonInheritedAnnotationInterface {
1089+
public interface SubNonInheritedAnnotationInterface extends NonInheritedAnnotationInterface {
10741090
}
10751091

1076-
public static interface SubSubNonInheritedAnnotationInterface extends SubNonInheritedAnnotationInterface {
1092+
public interface SubSubNonInheritedAnnotationInterface extends SubNonInheritedAnnotationInterface {
10771093
}
10781094

10791095
@ConventionBasedComposedContextConfig(locations = "explicitDeclaration")
@@ -1144,4 +1160,8 @@ static class TestComponentScanClass {
11441160
static class SpringAppConfigClass {
11451161
}
11461162

1163+
@Resource(name = "x")
1164+
static class ResourceHolder {
1165+
}
1166+
11471167
}

0 commit comments

Comments
 (0)