From 9a36027ae1ba6d7bf81777f14c5027e3688a1b91 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Jul 2019 16:58:45 +0200 Subject: [PATCH] MethodParameter.equals checks nesting level and containing class Closes gh-23352 --- .../springframework/core/MethodParameter.java | 14 ++++-- .../core/MethodParameterTests.java | 49 ++++++++++++++++++- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 7e2ec47bf3..8717852cc2 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import kotlin.reflect.jvm.ReflectJvmMapping; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; /** * Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method} @@ -61,6 +62,7 @@ public class MethodParameter { private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + private final Executable executable; private final int parameterIndex; @@ -68,7 +70,7 @@ public class MethodParameter { @Nullable private volatile Parameter parameter; - private int nestingLevel = 1; + private int nestingLevel; /** Map from Integer level to Integer type index. */ @Nullable @@ -658,12 +660,16 @@ public class MethodParameter { return false; } MethodParameter otherParam = (MethodParameter) other; - return (this.parameterIndex == otherParam.parameterIndex && getExecutable().equals(otherParam.getExecutable())); + return (this.containingClass == otherParam.containingClass && + ObjectUtils.nullSafeEquals(this.typeIndexesPerLevel, otherParam.typeIndexesPerLevel) && + this.nestingLevel == otherParam.nestingLevel && + this.parameterIndex == otherParam.parameterIndex && + this.executable.equals(otherParam.executable)); } @Override public int hashCode() { - return (getExecutable().hashCode() * 31 + this.parameterIndex); + return (31 * this.executable.hashCode() + this.parameterIndex); } @Override diff --git a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java index 035be3b0c7..75fb883523 100644 --- a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java +++ b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.concurrent.Callable; import org.junit.Before; @@ -148,6 +149,44 @@ public class MethodParameterTests { methodParameter.getGenericParameterType()); } + @Test + public void multipleResolveParameterTypeCalls() throws Exception { + Method method = ArrayList.class.getMethod("get", int.class); + MethodParameter methodParameter = MethodParameter.forExecutable(method, -1); + assertEquals(Object.class, methodParameter.getParameterType()); + GenericTypeResolver.resolveParameterType(methodParameter, StringList.class); + assertEquals(String.class, methodParameter.getParameterType()); + GenericTypeResolver.resolveParameterType(methodParameter, IntegerList.class); + assertEquals(Integer.class, methodParameter.getParameterType()); + } + + @Test + public void equalsAndHashCodeConsidersContainingClass() throws Exception { + Method method = ArrayList.class.getMethod("get", int.class); + MethodParameter m1 = MethodParameter.forExecutable(method, -1); + MethodParameter m2 = MethodParameter.forExecutable(method, -1); + MethodParameter m3 = MethodParameter.forExecutable(method, -1).nested(); + assertEquals(m1, m2); + assertNotEquals(m1, m3); + assertEquals(m1.hashCode(), m2.hashCode()); + } + + @Test + public void equalsAndHashCodeConsidersNesting() throws Exception { + Method method = ArrayList.class.getMethod("get", int.class); + MethodParameter m1 = MethodParameter.forExecutable(method, -1); + GenericTypeResolver.resolveParameterType(m1, StringList.class); + MethodParameter m2 = MethodParameter.forExecutable(method, -1); + GenericTypeResolver.resolveParameterType(m2, StringList.class); + MethodParameter m3 = MethodParameter.forExecutable(method, -1); + GenericTypeResolver.resolveParameterType(m3, IntegerList.class); + MethodParameter m4 = MethodParameter.forExecutable(method, -1); + assertEquals(m1, m2); + assertNotEquals(m1, m3); + assertNotEquals(m1, m4); + assertEquals(m1.hashCode(), m2.hashCode()); + } + public int method(String p1, long p2) { return 42; @@ -172,4 +211,12 @@ public class MethodParameterTests { private @interface Param { } + @SuppressWarnings("serial") + private static class StringList extends ArrayList { + } + + @SuppressWarnings("serial") + private static class IntegerList extends ArrayList { + } + }