ReflectiveMethodExecutor skips interface search (plus related polishing)

This commit is contained in:
Juergen Hoeller
2018-07-19 16:51:13 +02:00
parent c66f9d8880
commit 0c5c3103c6
5 changed files with 39 additions and 43 deletions

View File

@@ -182,8 +182,7 @@ public class MethodReference extends SpelNodeImpl {
@Nullable TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
List<MethodResolver> methodResolvers = evaluationContext.getMethodResolvers();
if (methodResolvers.size() != 1 ||
!(methodResolvers.get(0) instanceof ReflectiveMethodResolver)) {
if (methodResolvers.size() != 1 || !(methodResolvers.get(0) instanceof ReflectiveMethodResolver)) {
// Not a default ReflectiveMethodResolver - don't know whether caching is valid
return null;
}
@@ -249,7 +248,7 @@ public class MethodReference extends SpelNodeImpl {
Method method = ((ReflectiveMethodExecutor) executorToCheck.get()).getMethod();
String descriptor = CodeFlow.toDescriptor(method.getReturnType());
if (this.nullSafe && CodeFlow.isPrimitive(descriptor)) {
originalPrimitiveExitTypeDescriptor = descriptor;
this.originalPrimitiveExitTypeDescriptor = descriptor;
this.exitTypeDescriptor = CodeFlow.toBoxedDescriptor(descriptor);
}
else {
@@ -301,7 +300,7 @@ public class MethodReference extends SpelNodeImpl {
return true;
}
@Override
public void generateCode(MethodVisitor mv, CodeFlow cf) {
CachedMethodExecutor executorToCheck = this.cachedExecutor;
@@ -332,7 +331,7 @@ public class MethodReference extends SpelNodeImpl {
// Something on the stack when nothing is needed
mv.visitInsn(POP);
}
if (CodeFlow.isPrimitive(descriptor)) {
CodeFlow.insertBoxIfNecessary(mv, descriptor.charAt(0));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -29,6 +29,8 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
/**
* {@link MethodExecutor} that works via reflection.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@@ -48,6 +50,10 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
private boolean argumentConversionOccurred = false;
/**
* Create a new executor for the given method.
* @param method the method to invoke
*/
public ReflectiveMethodExecutor(Method method) {
this.method = method;
if (method.isVarArgs()) {
@@ -60,6 +66,9 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
}
/**
* Return the original method that this executor has been configured for.
*/
public Method getMethod() {
return this.method;
}
@@ -68,21 +77,22 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
* Find the first public class in the methods declaring class hierarchy that declares this method.
* Sometimes the reflective method discovery logic finds a suitable method that can easily be
* called via reflection but cannot be called from generated code when compiling the expression
* because of visibility restrictions. For example if a non public class overrides toString(), this
* helper method will walk up the type hierarchy to find the first public type that declares the
* method (if there is one!). For toString() it may walk as far as Object.
* because of visibility restrictions. For example if a non-public class overrides toString(),
* this helper method will walk up the type hierarchy to find the first public type that declares
* the method (if there is one!). For toString() it may walk as far as Object.
*/
@Nullable
public Class<?> getPublicDeclaringClass() {
if (!this.computedPublicDeclaringClass) {
this.publicDeclaringClass = discoverPublicClass(this.method, this.method.getDeclaringClass());
this.publicDeclaringClass =
discoverPublicDeclaringClass(this.method, this.method.getDeclaringClass());
this.computedPublicDeclaringClass = true;
}
return this.publicDeclaringClass;
}
@Nullable
private Class<?> discoverPublicClass(Method method, Class<?> clazz) {
private Class<?> discoverPublicDeclaringClass(Method method, Class<?> clazz) {
if (Modifier.isPublic(clazz.getModifiers())) {
try {
clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
@@ -92,12 +102,8 @@ public class ReflectiveMethodExecutor implements MethodExecutor {
// Continue below...
}
}
Class<?>[] ifcs = clazz.getInterfaces();
for (Class<?> ifc: ifcs) {
discoverPublicClass(method, ifc);
}
if (clazz.getSuperclass() != null) {
return discoverPublicClass(method, clazz.getSuperclass());
return discoverPublicDeclaringClass(method, clazz.getSuperclass());
}
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2018 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.
@@ -16,7 +16,6 @@
package org.springframework.expression.spel;
import org.junit.Before;
import org.junit.Test;
import org.springframework.expression.Expression;
@@ -36,17 +35,11 @@ public class CachedMethodExecutorTests {
private final ExpressionParser parser = new SpelExpressionParser();
private StandardEvaluationContext context;
@Before
public void setUp() throws Exception {
this.context = new StandardEvaluationContext(new RootObject());
}
private final StandardEvaluationContext context = new StandardEvaluationContext(new RootObject());
@Test
public void testCachedExecutionForParameters() throws Exception {
public void testCachedExecutionForParameters() {
Expression expression = this.parser.parseExpression("echo(#var)");
assertMethodExecution(expression, 42, "int: 42");
@@ -56,7 +49,7 @@ public class CachedMethodExecutorTests {
}
@Test
public void testCachedExecutionForTarget() throws Exception {
public void testCachedExecutionForTarget() {
Expression expression = this.parser.parseExpression("#var.echo(42)");
assertMethodExecution(expression, new RootObject(), "int: 42");
@@ -76,7 +69,6 @@ public class CachedMethodExecutorTests {
public String echo(String value) {
return "String: " + value;
}
}
public static class RootObject extends BaseObject {
@@ -84,7 +76,6 @@ public class CachedMethodExecutorTests {
public String echo(int value) {
return "int: " + value;
}
}
}