提交 7bf609f1 编写于 作者: S Sam Brannen

Implement equals() for synthesized annotations

Issue: SPR-13065
上级 c622f4c4
......@@ -21,13 +21,14 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.core.annotation.AnnotationUtils.*;
/**
* {@link InvocationHandler} for an {@link Annotation} that Spring has
* <em>synthesized</em> (i.e., wrapped in a dynamic proxy) with additional
......@@ -56,7 +57,7 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
private final Map<String, String> aliasMap;
public SynthesizedAnnotationInvocationHandler(AnnotatedElement annotatedElement, Annotation annotation,
SynthesizedAnnotationInvocationHandler(AnnotatedElement annotatedElement, Annotation annotation,
Map<String, String> aliasMap) {
this.annotatedElement = annotatedElement;
this.annotation = annotation;
......@@ -70,7 +71,10 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
Class<?>[] parameterTypes = method.getParameterTypes();
int parameterCount = parameterTypes.length;
if ("toString".equals(methodName) && (parameterCount == 0)) {
if ("equals".equals(methodName) && (parameterCount == 1) && (parameterTypes[0] == Object.class)) {
return equals(proxy, args[0]);
}
else if ("toString".equals(methodName) && (parameterCount == 0)) {
return toString(proxy);
}
......@@ -82,7 +86,7 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
ReflectionUtils.makeAccessible(method);
Object value = ReflectionUtils.invokeMethod(method, this.annotation, args);
// Nothing special to do?
// No custom processing necessary?
if (!aliasPresent && !nestedAnnotation) {
return value;
}
......@@ -101,11 +105,12 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
ReflectionUtils.makeAccessible(aliasedMethod);
Object aliasedValue = ReflectionUtils.invokeMethod(aliasedMethod, this.annotation, args);
Object defaultValue = AnnotationUtils.getDefaultValue(this.annotation, methodName);
Object defaultValue = getDefaultValue(this.annotation, methodName);
if (!ObjectUtils.nullSafeEquals(value, aliasedValue) && !ObjectUtils.nullSafeEquals(value, defaultValue)
&& !ObjectUtils.nullSafeEquals(aliasedValue, defaultValue)) {
String elementName = (this.annotatedElement == null ? "unknown element" : this.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.",
......@@ -123,23 +128,41 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler {
// Synthesize nested annotations before returning them.
if (value instanceof Annotation) {
value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.annotatedElement);
value = 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(annotations[i], this.annotatedElement);
annotations[i] = synthesizeAnnotation(annotations[i], this.annotatedElement);
}
}
return value;
}
private boolean equals(Object proxy, Object other) {
if (this == other) {
return true;
}
if (!this.annotationType.isInstance(other)) {
return false;
}
for (Method attributeMethod : getAttributeMethods(this.annotationType)) {
Object thisValue = ReflectionUtils.invokeMethod(attributeMethod, proxy);
Object otherValue = ReflectionUtils.invokeMethod(attributeMethod, other);
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
return false;
}
}
return true;
}
private String toString(Object proxy) {
StringBuilder sb = new StringBuilder("@").append(annotationType.getName()).append("(");
List<Method> attributeMethods = AnnotationUtils.getAttributeMethods(this.annotationType);
Iterator<Method> iterator = attributeMethods.iterator();
Iterator<Method> iterator = getAttributeMethods(this.annotationType).iterator();
while (iterator.hasNext()) {
Method attributeMethod = iterator.next();
sb.append(attributeMethod.getName());
......
......@@ -656,6 +656,44 @@ public class AnnotationUtilsTests {
assertThat(string, endsWith(")"));
}
@Test
public void equalsForSynthesizedAnnotations() throws Exception {
Method methodWithPath = WebController.class.getMethod("handleMappedWithPathAttribute");
WebMapping webMappingWithAliases = methodWithPath.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithAliases);
Method methodWithPathAndValue = WebController.class.getMethod("handleMappedWithSamePathAndValueAttributes");
WebMapping webMappingWithPathAndValue = methodWithPathAndValue.getAnnotation(WebMapping.class);
assertNotNull(webMappingWithPathAndValue);
WebMapping synthesizedWebMapping1 = synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping1);
WebMapping synthesizedWebMapping2 = synthesizeAnnotation(webMappingWithAliases);
assertNotNull(synthesizedWebMapping2);
// Equality amongst standard annotations
assertThat(webMappingWithAliases, is(webMappingWithAliases));
assertThat(webMappingWithPathAndValue, is(webMappingWithPathAndValue));
// Inequality amongst standard annotations
assertThat(webMappingWithAliases, is(not(webMappingWithPathAndValue)));
assertThat(webMappingWithPathAndValue, is(not(webMappingWithAliases)));
// Equality amongst synthesized annotations
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping1));
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping2));
assertThat(synthesizedWebMapping1, is(synthesizedWebMapping2));
assertThat(synthesizedWebMapping2, is(synthesizedWebMapping1));
// Equality between standard and synthesized annotations
assertThat(synthesizedWebMapping1, is(webMappingWithPathAndValue));
assertThat(webMappingWithPathAndValue, is(synthesizedWebMapping1));
// Inequality between standard and synthesized annotations
assertThat(synthesizedWebMapping1, is(not(webMappingWithAliases)));
assertThat(webMappingWithAliases, is(not(synthesizedWebMapping1)));
}
/**
* Fully reflection-based test that verifies support for
* {@linkplain AnnotationUtils#synthesizeAnnotation synthesizing annotations}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册