From 36682b7ad2f86c09deea5467a59347efb4e296f1 Mon Sep 17 00:00:00 2001 From: mrcoffee77 Date: Thu, 25 Jun 2020 11:02:17 +0200 Subject: [PATCH] Ensure methods declared in Object can be invoked on a JDK proxy in SpEL This commit ensures that methods declared in java.lang.Object (such as toString() can be invoked on a JDK proxy instance in a SpEL expression. Closes gh-25316 --- .../support/ReflectiveMethodResolver.java | 16 +++++++------- .../spel/support/ReflectionHelperTests.java | 21 +++++++++++++++---- 2 files changed, 25 insertions(+), 12 deletions(-) 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 765d52050766..be815cd0b870 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,13 +19,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; -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 java.util.*; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.MethodParameter; @@ -266,7 +260,13 @@ else if (Proxy.isProxyClass(type)) { * @since 3.1.1 */ protected Method[] getMethods(Class type) { - return type.getMethods(); + 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; } /** 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 c651d41d47b0..aa9c75f9f3a6 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,21 +18,21 @@ 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; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.core.convert.TypeDescriptor; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.ParseException; -import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.TypedValue; +import org.springframework.expression.*; 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; @@ -364,6 +364,19 @@ public void testOptimalReflectivePropertyAccessor() throws Exception { field.write(ctx, tester, "field", null)); } + @Test + void testReflectiveMethodResolver() throws AccessException { + 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."); + } /** * Used to validate the match returned from a compareArguments call.