parent
05d475a275
commit
ab92f4ed3a
|
@ -932,7 +932,7 @@ public class AnnotatedElementUtils {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
|
public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
|
||||||
annotation = AnnotationUtils.synthesizeAnnotation(annotation);
|
annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||||
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
|
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
|
||||||
|
|
||||||
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
|
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
|
||||||
|
|
|
@ -140,7 +140,7 @@ public abstract class AnnotationUtils {
|
||||||
}
|
}
|
||||||
Class<? extends Annotation> annotatedElement = ann.annotationType();
|
Class<? extends Annotation> annotatedElement = ann.annotationType();
|
||||||
try {
|
try {
|
||||||
return synthesizeAnnotation(annotatedElement, annotatedElement.getAnnotation(annotationType));
|
return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
// Assuming nested Class values not resolvable within annotation attributes...
|
// Assuming nested Class values not resolvable within annotation attributes...
|
||||||
|
@ -163,16 +163,16 @@ public abstract class AnnotationUtils {
|
||||||
*/
|
*/
|
||||||
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||||
try {
|
try {
|
||||||
A ann = annotatedElement.getAnnotation(annotationType);
|
A annotation = annotatedElement.getAnnotation(annotationType);
|
||||||
if (ann == null) {
|
if (annotation == null) {
|
||||||
for (Annotation metaAnn : annotatedElement.getAnnotations()) {
|
for (Annotation metaAnn : annotatedElement.getAnnotations()) {
|
||||||
ann = metaAnn.annotationType().getAnnotation(annotationType);
|
annotation = metaAnn.annotationType().getAnnotation(annotationType);
|
||||||
if (ann != null) {
|
if (annotation != null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return synthesizeAnnotation(annotatedElement, ann);
|
return synthesizeAnnotation(annotation, annotatedElement);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
// Assuming nested Class values not resolvable within annotation attributes...
|
// Assuming nested Class values not resolvable within annotation attributes...
|
||||||
|
@ -319,8 +319,8 @@ public abstract class AnnotationUtils {
|
||||||
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
|
||||||
// Do NOT store result in the findAnnotationCache since doing so could break
|
// Do NOT store result in the findAnnotationCache since doing so could break
|
||||||
// findAnnotation(Class, Class) and findAnnotation(Method, Class).
|
// findAnnotation(Class, Class) and findAnnotation(Method, Class).
|
||||||
return synthesizeAnnotation(annotatedElement,
|
return synthesizeAnnotation(findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()),
|
||||||
findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()));
|
annotatedElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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) {
|
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) {
|
||||||
|
@ -487,7 +487,7 @@ public abstract class AnnotationUtils {
|
||||||
findAnnotationCache.put(cacheKey, result);
|
findAnnotationCache.put(cacheKey, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return synthesizeAnnotation(clazz, result);
|
return synthesizeAnnotation(result, clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -820,7 +820,7 @@ public abstract class AnnotationUtils {
|
||||||
boolean defaultValuesAsPlaceholder, boolean synthesizeAnnotation) {
|
boolean defaultValuesAsPlaceholder, boolean synthesizeAnnotation) {
|
||||||
|
|
||||||
if (synthesizeAnnotation) {
|
if (synthesizeAnnotation) {
|
||||||
annotation = synthesizeAnnotation(annotatedElement, annotation);
|
annotation = synthesizeAnnotation(annotation, annotatedElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
@ -854,7 +854,7 @@ public abstract class AnnotationUtils {
|
||||||
/**
|
/**
|
||||||
* Adapt the given value according to the given class and nested annotation settings.
|
* Adapt the given value according to the given class and nested annotation settings.
|
||||||
* <p>Nested annotations will be
|
* <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
|
* @param annotatedElement the element that is annotated, used for contextual
|
||||||
* logging; may be {@code null} if unknown
|
* logging; may be {@code null} if unknown
|
||||||
* @param value the annotation attribute value
|
* @param value the annotation attribute value
|
||||||
|
@ -892,7 +892,7 @@ public abstract class AnnotationUtils {
|
||||||
nestedAnnotationsAsMap);
|
nestedAnnotationsAsMap);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return synthesizeAnnotation(annotatedElement, annotation);
|
return synthesizeAnnotation(annotation, annotatedElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -910,7 +910,7 @@ public abstract class AnnotationUtils {
|
||||||
else {
|
else {
|
||||||
Annotation[] synthesizedAnnotations = new Annotation[annotations.length];
|
Annotation[] synthesizedAnnotations = new Annotation[annotations.length];
|
||||||
for (int i = 0; i < annotations.length; i++) {
|
for (int i = 0; i < annotations.length; i++) {
|
||||||
synthesizedAnnotations[i] = synthesizeAnnotation(annotatedElement, annotations[i]);
|
synthesizedAnnotations[i] = synthesizeAnnotation(annotations[i], annotatedElement);
|
||||||
}
|
}
|
||||||
return synthesizedAnnotations;
|
return synthesizedAnnotations;
|
||||||
}
|
}
|
||||||
|
@ -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
|
* @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
|
* @since 4.2
|
||||||
* @see #synthesizeAnnotation(AnnotatedElement, Annotation)
|
* @see #synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||||
*/
|
*/
|
||||||
public static <A extends Annotation> A synthesizeAnnotation(A annotation) {
|
static <A extends Annotation> A synthesizeAnnotation(A annotation) {
|
||||||
return synthesizeAnnotation(null, 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 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
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@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) {
|
if (annotation == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1438,7 +1454,7 @@ public abstract class AnnotationUtils {
|
||||||
for (Annotation ann : element.getAnnotations()) {
|
for (Annotation ann : element.getAnnotations()) {
|
||||||
Class<? extends Annotation> currentAnnotationType = ann.annotationType();
|
Class<? extends Annotation> currentAnnotationType = ann.annotationType();
|
||||||
if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
|
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)) {
|
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) {
|
||||||
this.result.addAll(getValue(element, ann));
|
this.result.addAll(getValue(element, ann));
|
||||||
|
@ -1463,7 +1479,7 @@ public abstract class AnnotationUtils {
|
||||||
|
|
||||||
List<A> synthesizedAnnotations = new ArrayList<A>();
|
List<A> synthesizedAnnotations = new ArrayList<A>();
|
||||||
for (A anno : annotations) {
|
for (A anno : annotations) {
|
||||||
synthesizedAnnotations.add(synthesizeAnnotation(element, anno));
|
synthesizedAnnotations.add(synthesizeAnnotation(anno, element));
|
||||||
}
|
}
|
||||||
return synthesizedAnnotations;
|
return synthesizedAnnotations;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,20 @@ import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
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
|
* @author Sam Brannen
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
|
* @see AliasFor
|
||||||
|
* @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement)
|
||||||
*/
|
*/
|
||||||
class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||||
|
|
||||||
|
@ -37,18 +47,17 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||||
|
|
||||||
private final Annotation annotation;
|
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,
|
public SynthesizedAnnotationInvocationHandler(AnnotatedElement annotatedElement, Annotation annotation,
|
||||||
Map<String, String> aliasPairs) {
|
Map<String, String> aliasMap) {
|
||||||
this.annotatedElement = annotatedElement;
|
this.annotatedElement = annotatedElement;
|
||||||
this.annotation = annotation;
|
this.annotation = annotation;
|
||||||
this.aliasPairs = aliasPairs;
|
this.annotationType = annotation.annotationType();
|
||||||
|
this.aliasMap = aliasMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -56,8 +65,8 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||||
String attributeName = method.getName();
|
String attributeName = method.getName();
|
||||||
Class<?> returnType = method.getReturnType();
|
Class<?> returnType = method.getReturnType();
|
||||||
boolean nestedAnnotation = (Annotation[].class.isAssignableFrom(returnType) || Annotation.class.isAssignableFrom(returnType));
|
boolean nestedAnnotation = (Annotation[].class.isAssignableFrom(returnType) || Annotation.class.isAssignableFrom(returnType));
|
||||||
String aliasedAttributeName = aliasPairs.get(attributeName);
|
String aliasedAttributeName = aliasMap.get(attributeName);
|
||||||
boolean aliasPresent = aliasedAttributeName != null;
|
boolean aliasPresent = (aliasedAttributeName != null);
|
||||||
|
|
||||||
ReflectionUtils.makeAccessible(method);
|
ReflectionUtils.makeAccessible(method);
|
||||||
Object value = ReflectionUtils.invokeMethod(method, this.annotation, args);
|
Object value = ReflectionUtils.invokeMethod(method, this.annotation, args);
|
||||||
|
@ -70,26 +79,26 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||||
if (aliasPresent) {
|
if (aliasPresent) {
|
||||||
Method aliasedMethod = null;
|
Method aliasedMethod = null;
|
||||||
try {
|
try {
|
||||||
aliasedMethod = annotation.annotationType().getDeclaredMethod(aliasedAttributeName);
|
aliasedMethod = this.annotationType.getDeclaredMethod(aliasedAttributeName);
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException e) {
|
catch (NoSuchMethodException e) {
|
||||||
String msg = String.format("In annotation [%s], attribute [%s] is declared as an @AliasFor [%s], "
|
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);
|
aliasedAttributeName, aliasedAttributeName);
|
||||||
throw new AnnotationConfigurationException(msg);
|
throw new AnnotationConfigurationException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReflectionUtils.makeAccessible(aliasedMethod);
|
ReflectionUtils.makeAccessible(aliasedMethod);
|
||||||
Object aliasedValue = ReflectionUtils.invokeMethod(aliasedMethod, this.annotation, args);
|
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)
|
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !ObjectUtils.nullSafeEquals(value, defaultValue)
|
||||||
&& !ObjectUtils.nullSafeEquals(aliasedValue, 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(
|
String msg = String.format(
|
||||||
"In annotation [%s] declared on [%s], attribute [%s] and its alias [%s] are "
|
"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.",
|
+ "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));
|
ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue));
|
||||||
throw new AnnotationConfigurationException(msg);
|
throw new AnnotationConfigurationException(msg);
|
||||||
}
|
}
|
||||||
|
@ -103,12 +112,12 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
|
||||||
|
|
||||||
// Synthesize nested annotations before returning them.
|
// Synthesize nested annotations before returning them.
|
||||||
if (value instanceof Annotation) {
|
if (value instanceof Annotation) {
|
||||||
value = AnnotationUtils.synthesizeAnnotation(annotatedElement, (Annotation) value);
|
value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.annotatedElement);
|
||||||
}
|
}
|
||||||
else if (value instanceof Annotation[]) {
|
else if (value instanceof Annotation[]) {
|
||||||
Annotation[] annotations = (Annotation[]) value;
|
Annotation[] annotations = (Annotation[]) value;
|
||||||
for (int i = 0; i < annotations.length; i++) {
|
for (int i = 0; i < annotations.length; i++) {
|
||||||
annotations[i] = AnnotationUtils.synthesizeAnnotation(annotatedElement, annotations[i]);
|
annotations[i] = AnnotationUtils.synthesizeAnnotation(annotations[i], this.annotatedElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue