2020import java .lang .reflect .AnnotatedElement ;
2121import java .lang .reflect .InvocationHandler ;
2222import java .lang .reflect .Method ;
23+ import java .util .Arrays ;
2324import java .util .Iterator ;
2425import java .util .Map ;
2526
@@ -70,6 +71,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
7071 if (isEqualsMethod (method )) {
7172 return equals (proxy , args [0 ]);
7273 }
74+ if (isHashCodeMethod (method )) {
75+ return hashCode (proxy );
76+ }
7377 if (isToStringMethod (method )) {
7478 return toString (proxy );
7579 }
@@ -137,6 +141,12 @@ else if (value instanceof Annotation[]) {
137141 return value ;
138142 }
139143
144+ /**
145+ * See {@link Annotation#equals(Object)} for a definition of the required algorithm.
146+ *
147+ * @param proxy the synthesized annotation
148+ * @param other the other object to compare against
149+ */
140150 private boolean equals (Object proxy , Object other ) {
141151 if (this == other ) {
142152 return true ;
@@ -156,6 +166,72 @@ private boolean equals(Object proxy, Object other) {
156166 return true ;
157167 }
158168
169+ /**
170+ * See {@link Annotation#hashCode()} for a definition of the required algorithm.
171+ *
172+ * @param proxy the synthesized annotation
173+ */
174+ private int hashCode (Object proxy ) {
175+ int result = 0 ;
176+
177+ for (Method attributeMethod : getAttributeMethods (this .annotationType )) {
178+ Object value = invokeMethod (attributeMethod , proxy );
179+ int hashCode ;
180+ if (value .getClass ().isArray ()) {
181+ hashCode = hashCodeForArray (value );
182+ }
183+ else {
184+ hashCode = value .hashCode ();
185+ }
186+ result += (127 * attributeMethod .getName ().hashCode ()) ^ hashCode ;
187+ }
188+
189+ return result ;
190+ }
191+
192+ /**
193+ * WARNING: we can NOT use any of the {@code nullSafeHashCode()} methods
194+ * in Spring's {@link ObjectUtils} because those hash code generation
195+ * algorithms do not comply with the requirements specified in
196+ * {@link Annotation#hashCode()}.
197+ *
198+ * @param array the array to compute the hash code for
199+ */
200+ private int hashCodeForArray (Object array ) {
201+ if (array instanceof boolean []) {
202+ return Arrays .hashCode ((boolean []) array );
203+ }
204+ if (array instanceof byte []) {
205+ return Arrays .hashCode ((byte []) array );
206+ }
207+ if (array instanceof char []) {
208+ return Arrays .hashCode ((char []) array );
209+ }
210+ if (array instanceof double []) {
211+ return Arrays .hashCode ((double []) array );
212+ }
213+ if (array instanceof float []) {
214+ return Arrays .hashCode ((float []) array );
215+ }
216+ if (array instanceof int []) {
217+ return Arrays .hashCode ((int []) array );
218+ }
219+ if (array instanceof long []) {
220+ return Arrays .hashCode ((long []) array );
221+ }
222+ if (array instanceof short []) {
223+ return Arrays .hashCode ((short []) array );
224+ }
225+
226+ // else
227+ return Arrays .hashCode ((Object []) array );
228+ }
229+
230+ /**
231+ * See {@link Annotation#toString()} for guidelines on the recommended format.
232+ *
233+ * @param proxy the synthesized annotation
234+ */
159235 private String toString (Object proxy ) {
160236 StringBuilder sb = new StringBuilder ("@" ).append (annotationType .getName ()).append ("(" );
161237
0 commit comments