diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java index 95a5a2a26c82..5d756d07d4e2 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java @@ -19,7 +19,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.MethodParameter; @@ -42,6 +48,7 @@ * @author Andy Clement * @author Juergen Hoeller * @author Chris Beams + * @author Sam Brannen * @since 3.0 * @see StandardEvaluationContext#addMethodResolver(MethodResolver) */ @@ -238,6 +245,12 @@ else if (Proxy.isProxyClass(type)) { } } } + // Ensure methods defined in java.lang.Object are exposed for JDK proxies. + for (Method method : getMethods(Object.class)) { + if (isCandidateForInvocation(method, type)) { + result.add(method); + } + } return result; } else { @@ -260,13 +273,7 @@ else if (Proxy.isProxyClass(type)) { * @since 3.1.1 */ protected Method[] getMethods(Class type) { - Set methods=new HashSet<>(); - methods.addAll(Arrays.asList(type.getMethods())); - //Add all methods of Object to have methods like toString on Proxy-Objects - methods.addAll(Arrays.asList(Object.class.getMethods())); - - Method[] methods1 = methods.toArray(new Method[0]); - return methods1; + return type.getMethods(); } /** diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java index 51e457367ce8..74fad1177a8a 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java @@ -18,7 +18,6 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; @@ -27,12 +26,16 @@ import org.junit.jupiter.api.Test; import org.springframework.core.convert.TypeDescriptor; -import org.springframework.expression.*; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.MethodExecutor; +import org.springframework.expression.MethodResolver; +import org.springframework.expression.ParseException; +import org.springframework.expression.PropertyAccessor; +import org.springframework.expression.TypedValue; import org.springframework.expression.spel.AbstractExpressionTests; import org.springframework.expression.spel.SpelUtilities; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind; -import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -365,17 +368,18 @@ public void testOptimalReflectivePropertyAccessor() throws Exception { } @Test - void testReflectiveMethodResolver() throws AccessException { - MethodResolver resolver=new ReflectiveMethodResolver(); + void reflectiveMethodResolverForJdkProxies() throws Exception { + Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { Runnable.class }, (p, m, args) -> null); + + MethodResolver resolver = new ReflectiveMethodResolver(); StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); - Object obj= Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Runnable.class}, new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - return null; - } - }); - MethodExecutor mexec=resolver.resolve(evaluationContext,obj,"toString",new ArrayList<>()); - Assert.notNull(mexec,"MethodExecutor should not be empty."); + + MethodExecutor bogus = resolver.resolve(evaluationContext, proxy, "bogus", List.of()); + assertThat(bogus).as("MethodExecutor for bogus()").isNull(); + MethodExecutor toString = resolver.resolve(evaluationContext, proxy, "toString", List.of()); + assertThat(toString).as("MethodExecutor for toString()").isNotNull(); + MethodExecutor hashCode = resolver.resolve(evaluationContext, proxy, "hashCode", List.of()); + assertThat(hashCode).as("MethodExecutor for hashCode()").isNotNull(); } /**