Browse Source

Document SynthesizedAnnotation support

Issue: SPR-11512
pull/808/head
Sam Brannen 10 years ago
parent
commit
ab92f4ed3a
  1. 2
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  2. 64
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  3. 41
      spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java

2
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -932,7 +932,7 @@ public class AnnotatedElementUtils { @@ -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<? extends Annotation> targetAnnotationType = attributes.annotationType();
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {

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

@ -140,7 +140,7 @@ public abstract class AnnotationUtils { @@ -140,7 +140,7 @@ public abstract class AnnotationUtils {
}
Class<? extends Annotation> 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 { @@ -163,16 +163,16 @@ public abstract class AnnotationUtils {
*/
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> 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 { @@ -319,8 +319,8 @@ public abstract class AnnotationUtils {
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> 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<Annotation>()));
return synthesizeAnnotation(findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()),
annotatedElement);
}
/**
@ -411,7 +411,7 @@ public abstract class AnnotationUtils { @@ -411,7 +411,7 @@ public abstract class AnnotationUtils {
}
}
return synthesizeAnnotation(method, result);
return synthesizeAnnotation(result, method);
}
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
@ -487,7 +487,7 @@ public abstract class AnnotationUtils { @@ -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 { @@ -820,7 +820,7 @@ public abstract class AnnotationUtils {
boolean defaultValuesAsPlaceholder, boolean synthesizeAnnotation) {
if (synthesizeAnnotation) {
annotation = synthesizeAnnotation(annotatedElement, annotation);
annotation = synthesizeAnnotation(annotation, annotatedElement);
}
Class<? extends Annotation> annotationType = annotation.annotationType();
@ -854,7 +854,7 @@ public abstract class AnnotationUtils { @@ -854,7 +854,7 @@ public abstract class AnnotationUtils {
/**
* Adapt the given value according to the given class and nested annotation settings.
* <p>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 { @@ -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 { @@ -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 { @@ -1009,26 +1009,42 @@ public abstract class AnnotationUtils {
}
/**
* TODO Document synthesizeAnnotation().
* <em>Synthesize</em> the supplied {@code annotation} by wrapping it in
* a dynamic proxy that transparently enforces <em>attribute alias</em>
* 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
* <em>synthesizable</em>; {@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 extends Annotation> A synthesizeAnnotation(A annotation) {
return synthesizeAnnotation(null, annotation);
static <A extends Annotation> A synthesizeAnnotation(A annotation) {
return synthesizeAnnotation(annotation, null);
}
/**
* TODO Document synthesizeAnnotation().
* <em>Synthesize</em> the supplied {@code annotation} by wrapping it in
* a dynamic proxy that transparently enforces <em>attribute alias</em>
* 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
* <em>synthesizable</em>; {@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 extends Annotation> A synthesizeAnnotation(AnnotatedElement annotatedElement, A annotation) {
public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) {
if (annotation == null) {
return null;
}
@ -1438,7 +1454,7 @@ public abstract class AnnotationUtils { @@ -1438,7 +1454,7 @@ public abstract class AnnotationUtils {
for (Annotation ann : element.getAnnotations()) {
Class<? extends Annotation> 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 { @@ -1463,7 +1479,7 @@ public abstract class AnnotationUtils {
List<A> synthesizedAnnotations = new ArrayList<A>();
for (A anno : annotations) {
synthesizedAnnotations.add(synthesizeAnnotation(element, anno));
synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
}
return synthesizedAnnotations;
}

41
spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java

@ -26,10 +26,20 @@ import org.springframework.util.ObjectUtils; @@ -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
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
* functionality.
*
* <p>{@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.,
* <em>lazily</em>).
*
* @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 { @@ -37,18 +47,17 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
private final Annotation annotation;
private final Map<String, String> aliasPairs;
private final Class<? extends Annotation> annotationType;
private final Map<String, String> aliasMap;
public SynthesizedAnnotationInvocationHandler(Annotation annotation, Map<String, String> aliasPairs) {
this(null, annotation, aliasPairs);
}
public SynthesizedAnnotationInvocationHandler(AnnotatedElement annotatedElement, Annotation annotation,
Map<String, String> aliasPairs) {
Map<String, String> 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 { @@ -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 { @@ -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 { @@ -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);
}
}

Loading…
Cancel
Save