|
|
|
@ -26,6 +26,7 @@ import org.springframework.expression.EvaluationContext; |
|
|
|
import org.springframework.expression.MethodExecutor; |
|
|
|
import org.springframework.expression.MethodExecutor; |
|
|
|
import org.springframework.expression.TypedValue; |
|
|
|
import org.springframework.expression.TypedValue; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
|
|
|
|
import org.springframework.util.ClassUtils; |
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
import org.springframework.util.ReflectionUtils; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -37,7 +38,9 @@ import org.springframework.util.ReflectionUtils; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
|
|
|
|
|
|
|
|
private final Method method; |
|
|
|
private final Method originalMethod; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Method methodToInvoke; |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private final Integer varargsPosition; |
|
|
|
private final Integer varargsPosition; |
|
|
|
@ -50,8 +53,13 @@ public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
private boolean argumentConversionOccurred = false; |
|
|
|
private boolean argumentConversionOccurred = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Create a new executor for the given method. |
|
|
|
|
|
|
|
* @param method the method to invoke |
|
|
|
|
|
|
|
*/ |
|
|
|
public ReflectiveMethodExecutor(Method method) { |
|
|
|
public ReflectiveMethodExecutor(Method method) { |
|
|
|
this.method = method; |
|
|
|
this.originalMethod = method; |
|
|
|
|
|
|
|
this.methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(method); |
|
|
|
if (method.isVarArgs()) { |
|
|
|
if (method.isVarArgs()) { |
|
|
|
Class<?>[] paramTypes = method.getParameterTypes(); |
|
|
|
Class<?>[] paramTypes = method.getParameterTypes(); |
|
|
|
this.varargsPosition = paramTypes.length - 1; |
|
|
|
this.varargsPosition = paramTypes.length - 1; |
|
|
|
@ -62,29 +70,33 @@ public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public Method getMethod() { |
|
|
|
/** |
|
|
|
return this.method; |
|
|
|
* Return the original method that this executor has been configured for. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public final Method getMethod() { |
|
|
|
|
|
|
|
return this.originalMethod; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Find the first public class in the methods declaring class hierarchy that declares this method. |
|
|
|
* 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 |
|
|
|
* 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 |
|
|
|
* 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 |
|
|
|
* because of visibility restrictions. For example if a non-public class overrides toString(), |
|
|
|
* helper method will walk up the type hierarchy to find the first public type that declares the |
|
|
|
* this helper method will walk up the type hierarchy to find the first public type that declares |
|
|
|
* method (if there is one!). For toString() it may walk as far as Object. |
|
|
|
* the method (if there is one!). For toString() it may walk as far as Object. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
public Class<?> getPublicDeclaringClass() { |
|
|
|
public Class<?> getPublicDeclaringClass() { |
|
|
|
if (!this.computedPublicDeclaringClass) { |
|
|
|
if (!this.computedPublicDeclaringClass) { |
|
|
|
this.publicDeclaringClass = discoverPublicClass(this.method, this.method.getDeclaringClass()); |
|
|
|
this.publicDeclaringClass = |
|
|
|
|
|
|
|
discoverPublicDeclaringClass(this.originalMethod, this.originalMethod.getDeclaringClass()); |
|
|
|
this.computedPublicDeclaringClass = true; |
|
|
|
this.computedPublicDeclaringClass = true; |
|
|
|
} |
|
|
|
} |
|
|
|
return this.publicDeclaringClass; |
|
|
|
return this.publicDeclaringClass; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Nullable |
|
|
|
@Nullable |
|
|
|
private Class<?> discoverPublicClass(Method method, Class<?> clazz) { |
|
|
|
private Class<?> discoverPublicDeclaringClass(Method method, Class<?> clazz) { |
|
|
|
if (Modifier.isPublic(clazz.getModifiers())) { |
|
|
|
if (Modifier.isPublic(clazz.getModifiers())) { |
|
|
|
try { |
|
|
|
try { |
|
|
|
clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); |
|
|
|
clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); |
|
|
|
@ -94,12 +106,8 @@ public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
// Continue below...
|
|
|
|
// Continue below...
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Class<?>[] ifcs = clazz.getInterfaces(); |
|
|
|
|
|
|
|
for (Class<?> ifc: ifcs) { |
|
|
|
|
|
|
|
discoverPublicClass(method, ifc); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (clazz.getSuperclass() != null) { |
|
|
|
if (clazz.getSuperclass() != null) { |
|
|
|
return discoverPublicClass(method, clazz.getSuperclass()); |
|
|
|
return discoverPublicDeclaringClass(method, clazz.getSuperclass()); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -113,17 +121,17 @@ public class ReflectiveMethodExecutor implements MethodExecutor { |
|
|
|
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { |
|
|
|
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { |
|
|
|
try { |
|
|
|
try { |
|
|
|
this.argumentConversionOccurred = ReflectionHelper.convertArguments( |
|
|
|
this.argumentConversionOccurred = ReflectionHelper.convertArguments( |
|
|
|
context.getTypeConverter(), arguments, this.method, this.varargsPosition); |
|
|
|
context.getTypeConverter(), arguments, this.originalMethod, this.varargsPosition); |
|
|
|
if (this.method.isVarArgs()) { |
|
|
|
if (this.originalMethod.isVarArgs()) { |
|
|
|
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation( |
|
|
|
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation( |
|
|
|
this.method.getParameterTypes(), arguments); |
|
|
|
this.originalMethod.getParameterTypes(), arguments); |
|
|
|
} |
|
|
|
} |
|
|
|
ReflectionUtils.makeAccessible(this.method); |
|
|
|
ReflectionUtils.makeAccessible(this.methodToInvoke); |
|
|
|
Object value = this.method.invoke(target, arguments); |
|
|
|
Object value = this.methodToInvoke.invoke(target, arguments); |
|
|
|
return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrow(value)); |
|
|
|
return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.originalMethod, -1)).narrow(value)); |
|
|
|
} |
|
|
|
} |
|
|
|
catch (Exception ex) { |
|
|
|
catch (Exception ex) { |
|
|
|
throw new AccessException("Problem invoking method: " + this.method, ex); |
|
|
|
throw new AccessException("Problem invoking method: " + this.methodToInvoke, ex); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|