diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index f7eb74cd01..9265dbe580 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -514,7 +514,7 @@ public abstract class AnnotationUtils { try { Annotation[] anns = annotatedElement.getDeclaredAnnotations(); for (Annotation ann : anns) { - if (ann.annotationType().equals(annotationType)) { + if (ann.annotationType() == annotationType) { return (A) ann; } } @@ -703,7 +703,7 @@ public abstract class AnnotationUtils { try { Annotation[] anns = clazz.getDeclaredAnnotations(); for (Annotation ann : anns) { - if (ann.annotationType().equals(annotationType)) { + if (ann.annotationType() == annotationType) { return (A) ann; } } @@ -828,7 +828,7 @@ public abstract class AnnotationUtils { Assert.notNull(clazz, "Class must not be null"); try { for (Annotation ann : clazz.getDeclaredAnnotations()) { - if (ann.annotationType().equals(annotationType)) { + if (ann.annotationType() == annotationType) { return true; } } @@ -1360,7 +1360,8 @@ public abstract class AnnotationUtils { if (annotation == null) { return null; } - if (annotation instanceof SynthesizedAnnotation) { + if (annotation instanceof SynthesizedAnnotation || (Proxy.isProxyClass(annotation.getClass()) && + Proxy.getInvocationHandler(annotation) instanceof SynthesizedAnnotationInvocationHandler)) { return annotation; } @@ -1372,8 +1373,9 @@ public abstract class AnnotationUtils { DefaultAnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); - return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), - new Class[] {(Class) annotationType, SynthesizedAnnotation.class}, handler); + Class[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? + new Class[] {annotationType, SynthesizedAnnotation.class} : new Class[] {annotationType}); + return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler); } /** @@ -1418,8 +1420,9 @@ public abstract class AnnotationUtils { MapAnnotationAttributeExtractor attributeExtractor = new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); - return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), - new Class[] {annotationType, SynthesizedAnnotation.class}, handler); + Class[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? + new Class[] {annotationType, SynthesizedAnnotation.class} : new Class[] {annotationType}); + return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler); } /** @@ -1539,10 +1542,23 @@ public abstract class AnnotationUtils { } attributeAliasesCache.put(annotationType, map); - return map; } + /** + * Check whether we can expose our {@link SynthesizedAnnotation} marker for the given annotation type. + * @param annotationType the annotation type that we are about to create a synthesized proxy for + */ + private static boolean canExposeSynthesizedMarker(Class annotationType) { + try { + return (Class.forName(SynthesizedAnnotation.class.getName(), false, annotationType.getClassLoader()) == + SynthesizedAnnotation.class); + } + catch (ClassNotFoundException ex) { + return false; + } + } + /** * Determine if annotations of the supplied {@code annotationType} are * synthesizable (i.e., in need of being wrapped in a dynamic @@ -1629,7 +1645,7 @@ public abstract class AnnotationUtils { static String getAttributeOverrideName(Method attribute, Class metaAnnotationType) { Assert.notNull(attribute, "attribute must not be null"); Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null"); - Assert.isTrue(!Annotation.class.equals(metaAnnotationType), + Assert.isTrue(Annotation.class != metaAnnotationType, "metaAnnotationType must not be [java.lang.annotation.Annotation]"); AliasDescriptor descriptor = AliasDescriptor.from(attribute); @@ -1931,7 +1947,7 @@ public abstract class AnnotationUtils { this.sourceAnnotationType = (Class) declaringClass; this.sourceAttributeName = this.sourceAttribute.getName(); - this.aliasedAnnotationType = (Annotation.class.equals(aliasFor.annotation()) ? + this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ? this.sourceAnnotationType : aliasFor.annotation()); this.aliasedAttributeName = getAliasedAttributeName(aliasFor, this.sourceAttribute); try { @@ -1945,7 +1961,7 @@ public abstract class AnnotationUtils { throw new AnnotationConfigurationException(msg, ex); } - this.isAliasPair = this.sourceAnnotationType.equals(this.aliasedAnnotationType); + this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType); } private void validate() { @@ -1979,7 +1995,7 @@ public abstract class AnnotationUtils { Class returnType = this.sourceAttribute.getReturnType(); Class aliasedReturnType = this.aliasedAttribute.getReturnType(); - if (!returnType.equals(aliasedReturnType)) { + if (returnType != aliasedReturnType) { String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] " + "and attribute [%s] in annotation [%s] must declare the same return type.", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, @@ -2030,7 +2046,7 @@ public abstract class AnnotationUtils { * @see #isAliasFor */ private boolean isOverrideFor(Class metaAnnotationType) { - return this.aliasedAnnotationType.equals(metaAnnotationType); + return (this.aliasedAnnotationType == metaAnnotationType); } /** @@ -2087,7 +2103,7 @@ public abstract class AnnotationUtils { public String getAttributeOverrideName(Class metaAnnotationType) { Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null"); - Assert.isTrue(!Annotation.class.equals(metaAnnotationType), + Assert.isTrue(Annotation.class != metaAnnotationType, "metaAnnotationType must not be [java.lang.annotation.Annotation]"); // Search the attribute override hierarchy, starting with the current attribute diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java index 465104b0e5..57dd98478b 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotatedElementUtilsTests.java @@ -23,9 +23,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.Date; import java.util.List; import java.util.Set; +import javax.annotation.Resource; import org.junit.Ignore; import org.junit.Rule; @@ -632,7 +635,20 @@ public class AnnotatedElementUtilsTests { assertArrayEquals("locations for " + element, EMPTY, contextConfig.locations()); // 'value' in @SpringAppConfig should not override 'value' in @ContextConfig assertArrayEquals("value for " + element, EMPTY, contextConfig.value()); - assertArrayEquals("classes for " + element, new Class[] { Number.class }, contextConfig.classes()); + assertArrayEquals("classes for " + element, new Class[] {Number.class}, contextConfig.classes()); + } + + @Test + public void javaLangAnnotationTypeViaFindMergedAnnotation() throws Exception { + Constructor deprecatedCtor = Date.class.getConstructor(String.class); + assertEquals(deprecatedCtor.getAnnotation(Deprecated.class), findMergedAnnotation(deprecatedCtor, Deprecated.class)); + assertEquals(Date.class.getAnnotation(Deprecated.class), findMergedAnnotation(Date.class, Deprecated.class)); + } + + @Test + public void javaxAnnotationTypeViaFindMergedAnnotation() throws Exception { + assertEquals(ResourceHolder.class.getAnnotation(Resource.class), findMergedAnnotation(ResourceHolder.class, Resource.class)); + assertEquals(SpringAppConfigClass.class.getAnnotation(Resource.class), findMergedAnnotation(SpringAppConfigClass.class, Resource.class)); } @@ -685,7 +701,7 @@ public class AnnotatedElementUtilsTests { // ------------------------------------------------------------------------- @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE, ElementType.METHOD }) + @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @interface Transactional { @@ -697,7 +713,7 @@ public class AnnotatedElementUtilsTests { } @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE, ElementType.METHOD }) + @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited @interface AliasedTransactional { @@ -849,7 +865,7 @@ public class AnnotatedElementUtilsTests { String[] value() default {}; } - @ImplicitAliasesContextConfig(xmlFiles = { "A.xml", "B.xml" }) + @ImplicitAliasesContextConfig(xmlFiles = {"A.xml", "B.xml"}) @Retention(RetentionPolicy.RUNTIME) @interface ComposedImplicitAliasesContextConfig { } @@ -938,7 +954,7 @@ public class AnnotatedElementUtilsTests { String pattern(); } - @ComponentScan(excludeFilters = { @Filter(pattern = "*Test"), @Filter(pattern = "*Tests") }) + @ComponentScan(excludeFilters = {@Filter(pattern = "*Test"), @Filter(pattern = "*Tests")}) @Retention(RetentionPolicy.RUNTIME) @interface TestComponentScan { @@ -1037,7 +1053,7 @@ public class AnnotatedElementUtilsTests { } } - public static interface GenericParameter { + public interface GenericParameter { T getFor(Class cls); } @@ -1057,23 +1073,23 @@ public class AnnotatedElementUtilsTests { } @Transactional - public static interface InheritedAnnotationInterface { + public interface InheritedAnnotationInterface { } - public static interface SubInheritedAnnotationInterface extends InheritedAnnotationInterface { + public interface SubInheritedAnnotationInterface extends InheritedAnnotationInterface { } - public static interface SubSubInheritedAnnotationInterface extends SubInheritedAnnotationInterface { + public interface SubSubInheritedAnnotationInterface extends SubInheritedAnnotationInterface { } @Order - public static interface NonInheritedAnnotationInterface { + public interface NonInheritedAnnotationInterface { } - public static interface SubNonInheritedAnnotationInterface extends NonInheritedAnnotationInterface { + public interface SubNonInheritedAnnotationInterface extends NonInheritedAnnotationInterface { } - public static interface SubSubNonInheritedAnnotationInterface extends SubNonInheritedAnnotationInterface { + public interface SubSubNonInheritedAnnotationInterface extends SubNonInheritedAnnotationInterface { } @ConventionBasedComposedContextConfig(locations = "explicitDeclaration") @@ -1144,4 +1160,8 @@ public class AnnotatedElementUtilsTests { static class SpringAppConfigClass { } + @Resource(name = "x") + static class ResourceHolder { + } + }