diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index c56e72e779d..9ed31690380 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -932,7 +932,7 @@ public class AnnotatedElementUtils { @Override public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) { - annotation = AnnotationUtils.synthesizeAnnotation(annotation); + annotation = AnnotationUtils.synthesizeAnnotation(annotation, element); Class targetAnnotationType = attributes.annotationType(); for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) { 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 7e602c099cb..50ebcd032e2 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 @@ -140,7 +140,7 @@ public abstract class AnnotationUtils { } Class annotatedElement = ann.annotationType(); try { - return synthesizeAnnotation(annotatedElement, annotatedElement.getAnnotation(annotationType)); + return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement); } catch (Exception ex) { // Assuming nested Class values not resolvable within annotation attributes... @@ -163,16 +163,16 @@ public abstract class AnnotationUtils { */ public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { try { - A ann = annotatedElement.getAnnotation(annotationType); - if (ann == null) { + A annotation = annotatedElement.getAnnotation(annotationType); + if (annotation == null) { for (Annotation metaAnn : annotatedElement.getAnnotations()) { - ann = metaAnn.annotationType().getAnnotation(annotationType); - if (ann != null) { + annotation = metaAnn.annotationType().getAnnotation(annotationType); + if (annotation != null) { break; } } } - return synthesizeAnnotation(annotatedElement, ann); + return synthesizeAnnotation(annotation, annotatedElement); } catch (Exception ex) { // Assuming nested Class values not resolvable within annotation attributes... @@ -319,8 +319,8 @@ public abstract class AnnotationUtils { public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { // Do NOT store result in the findAnnotationCache since doing so could break // findAnnotation(Class, Class) and findAnnotation(Method, Class). - return synthesizeAnnotation(annotatedElement, - findAnnotation(annotatedElement, annotationType, new HashSet())); + return synthesizeAnnotation(findAnnotation(annotatedElement, annotationType, new HashSet()), + annotatedElement); } /** @@ -411,7 +411,7 @@ public abstract class AnnotationUtils { } } - return synthesizeAnnotation(method, result); + return synthesizeAnnotation(result, method); } private static A searchOnInterfaces(Method method, Class annotationType, Class... ifcs) { @@ -487,7 +487,7 @@ public abstract class AnnotationUtils { findAnnotationCache.put(cacheKey, result); } } - return synthesizeAnnotation(clazz, result); + return synthesizeAnnotation(result, clazz); } /** @@ -820,7 +820,7 @@ public abstract class AnnotationUtils { boolean defaultValuesAsPlaceholder, boolean synthesizeAnnotation) { if (synthesizeAnnotation) { - annotation = synthesizeAnnotation(annotatedElement, annotation); + annotation = synthesizeAnnotation(annotation, annotatedElement); } Class annotationType = annotation.annotationType(); @@ -854,7 +854,7 @@ public abstract class AnnotationUtils { /** * Adapt the given value according to the given class and nested annotation settings. *

Nested annotations will be - * {@linkplain #synthesizeAnnotation(AnnotatedElement, Annotation) synthesized}. + * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}. * @param annotatedElement the element that is annotated, used for contextual * logging; may be {@code null} if unknown * @param value the annotation attribute value @@ -892,7 +892,7 @@ public abstract class AnnotationUtils { nestedAnnotationsAsMap); } else { - return synthesizeAnnotation(annotatedElement, annotation); + return synthesizeAnnotation(annotation, annotatedElement); } } @@ -910,7 +910,7 @@ public abstract class AnnotationUtils { else { Annotation[] synthesizedAnnotations = new Annotation[annotations.length]; for (int i = 0; i < annotations.length; i++) { - synthesizedAnnotations[i] = synthesizeAnnotation(annotatedElement, annotations[i]); + synthesizedAnnotations[i] = synthesizeAnnotation(annotations[i], annotatedElement); } return synthesizedAnnotations; } @@ -1009,26 +1009,42 @@ public abstract class AnnotationUtils { } /** - * TODO Document synthesizeAnnotation(). + * Synthesize the supplied {@code annotation} by wrapping it in + * a dynamic proxy that transparently enforces attribute alias + * semantics for annotation attributes that are annotated with + * {@link AliasFor @AliasFor}. * * @param annotation the annotation to synthesize + * @return the synthesized annotation, if the supplied annotation is + * synthesizable; {@code null} if the supplied annotation is + * {@code null}; otherwise, the supplied annotation unmodified + * @throws AnnotationConfigurationException if invalid configuration of + * {@code @AliasFor} is detected * @since 4.2 - * @see #synthesizeAnnotation(AnnotatedElement, Annotation) + * @see #synthesizeAnnotation(Annotation, AnnotatedElement) */ - public static A synthesizeAnnotation(A annotation) { - return synthesizeAnnotation(null, annotation); + static A synthesizeAnnotation(A annotation) { + return synthesizeAnnotation(annotation, null); } /** - * TODO Document synthesizeAnnotation(). + * Synthesize the supplied {@code annotation} by wrapping it in + * a dynamic proxy that transparently enforces attribute alias + * semantics for annotation attributes that are annotated with + * {@link AliasFor @AliasFor}. * - * @param annotatedElement the element that is annotated with the supplied - * annotation, used for contextual logging; may be {@code null} if unknown * @param annotation the annotation to synthesize + * @param annotatedElement the element that is annotated with the supplied + * annotation; may be {@code null} if unknown + * @return the synthesized annotation, if the supplied annotation is + * synthesizable; {@code null} if the supplied annotation is + * {@code null}; otherwise, the supplied annotation unmodified + * @throws AnnotationConfigurationException if invalid configuration of + * {@code @AliasFor} is detected * @since 4.2 */ @SuppressWarnings("unchecked") - public static A synthesizeAnnotation(AnnotatedElement annotatedElement, A annotation) { + public static A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) { if (annotation == null) { return null; } @@ -1438,7 +1454,7 @@ public abstract class AnnotationUtils { for (Annotation ann : element.getAnnotations()) { Class currentAnnotationType = ann.annotationType(); if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) { - this.result.add(synthesizeAnnotation(element, (A) ann)); + this.result.add(synthesizeAnnotation((A) ann, element)); } else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) { this.result.addAll(getValue(element, ann)); @@ -1463,7 +1479,7 @@ public abstract class AnnotationUtils { List synthesizedAnnotations = new ArrayList(); for (A anno : annotations) { - synthesizedAnnotations.add(synthesizeAnnotation(element, anno)); + synthesizedAnnotations.add(synthesizeAnnotation(anno, element)); } return synthesizedAnnotations; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java index a4b6555a197..e6dfa647487 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java @@ -26,10 +26,20 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; /** - * TODO Document SynthesizedAnnotationInvocationHandler. + * {@link InvocationHandler} for an {@link Annotation} that Spring has + * synthesized (i.e., wrapped in a dynamic proxy) with additional + * functionality. + * + *

{@code SynthesizedAnnotationInvocationHandler} transparently enforces + * attribute alias semantics for annotation attributes that are annotated + * with {@link AliasFor @AliasFor}. In addition, nested annotations and + * arrays of nested annotations will be synthesized upon first access (i.e., + * lazily). * * @author Sam Brannen * @since 4.2 + * @see AliasFor + * @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement) */ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { @@ -37,18 +47,17 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { private final Annotation annotation; - private final Map aliasPairs; + private final Class annotationType; + private final Map aliasMap; - public SynthesizedAnnotationInvocationHandler(Annotation annotation, Map aliasPairs) { - this(null, annotation, aliasPairs); - } public SynthesizedAnnotationInvocationHandler(AnnotatedElement annotatedElement, Annotation annotation, - Map aliasPairs) { + Map aliasMap) { this.annotatedElement = annotatedElement; this.annotation = annotation; - this.aliasPairs = aliasPairs; + this.annotationType = annotation.annotationType(); + this.aliasMap = aliasMap; } @Override @@ -56,8 +65,8 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { String attributeName = method.getName(); Class returnType = method.getReturnType(); boolean nestedAnnotation = (Annotation[].class.isAssignableFrom(returnType) || Annotation.class.isAssignableFrom(returnType)); - String aliasedAttributeName = aliasPairs.get(attributeName); - boolean aliasPresent = aliasedAttributeName != null; + String aliasedAttributeName = aliasMap.get(attributeName); + boolean aliasPresent = (aliasedAttributeName != null); ReflectionUtils.makeAccessible(method); Object value = ReflectionUtils.invokeMethod(method, this.annotation, args); @@ -70,26 +79,26 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { if (aliasPresent) { Method aliasedMethod = null; try { - aliasedMethod = annotation.annotationType().getDeclaredMethod(aliasedAttributeName); + aliasedMethod = this.annotationType.getDeclaredMethod(aliasedAttributeName); } catch (NoSuchMethodException e) { String msg = String.format("In annotation [%s], attribute [%s] is declared as an @AliasFor [%s], " - + "but attribute [%s] does not exist.", annotation.annotationType().getName(), attributeName, + + "but attribute [%s] does not exist.", this.annotationType.getName(), attributeName, aliasedAttributeName, aliasedAttributeName); throw new AnnotationConfigurationException(msg); } ReflectionUtils.makeAccessible(aliasedMethod); Object aliasedValue = ReflectionUtils.invokeMethod(aliasedMethod, this.annotation, args); - Object defaultValue = AnnotationUtils.getDefaultValue(annotation, attributeName); + Object defaultValue = AnnotationUtils.getDefaultValue(this.annotation, attributeName); if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !ObjectUtils.nullSafeEquals(value, defaultValue) && !ObjectUtils.nullSafeEquals(aliasedValue, defaultValue)) { - String elementAsString = (annotatedElement == null ? "unknown element" : annotatedElement.toString()); + String elementName = (this.annotatedElement == null ? "unknown element" : this.annotatedElement.toString()); String msg = String.format( "In annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are " + "declared with values of [%s] and [%s], but only one declaration is permitted.", - annotation.annotationType().getName(), elementAsString, attributeName, aliasedAttributeName, + this.annotationType.getName(), elementName, attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue)); throw new AnnotationConfigurationException(msg); } @@ -103,12 +112,12 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { // Synthesize nested annotations before returning them. if (value instanceof Annotation) { - value = AnnotationUtils.synthesizeAnnotation(annotatedElement, (Annotation) value); + value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.annotatedElement); } else if (value instanceof Annotation[]) { Annotation[] annotations = (Annotation[]) value; for (int i = 0; i < annotations.length; i++) { - annotations[i] = AnnotationUtils.synthesizeAnnotation(annotatedElement, annotations[i]); + annotations[i] = AnnotationUtils.synthesizeAnnotation(annotations[i], this.annotatedElement); } }