提交 bf4563e2 编写于 作者: P Phillip Webb

Include target types in MethodReference cache

Update the cached MethodExecutor in MethodReference to include the
target type. Prevents the incorrect use of the cache when the
SpEL expression refers to a different target object.

Issue: SPR-10657
上级 60532cbd
...@@ -48,14 +48,12 @@ public class MethodReference extends SpelNodeImpl { ...@@ -48,14 +48,12 @@ public class MethodReference extends SpelNodeImpl {
private volatile CachedMethodExecutor cachedExecutor; private volatile CachedMethodExecutor cachedExecutor;
public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) { public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) {
super(pos, arguments); super(pos, arguments);
this.name = methodName; this.name = methodName;
this.nullSafe = nullSafe; this.nullSafe = nullSafe;
} }
public final String getName() { public final String getName() {
return this.name; return this.name;
} }
...@@ -102,6 +100,9 @@ public class MethodReference extends SpelNodeImpl { ...@@ -102,6 +100,9 @@ public class MethodReference extends SpelNodeImpl {
state.popActiveContextObject(); state.popActiveContextObject();
} }
} }
TypedValue activeContextObject = state.getActiveContextObject();
TypeDescriptor target = (activeContextObject == null ? null
: activeContextObject.getTypeDescriptor());
List<TypeDescriptor> argumentTypes = getTypes(arguments); List<TypeDescriptor> argumentTypes = getTypes(arguments);
if (currentContext.getValue() == null) { if (currentContext.getValue() == null) {
if (this.nullSafe) { if (this.nullSafe) {
...@@ -113,7 +114,7 @@ public class MethodReference extends SpelNodeImpl { ...@@ -113,7 +114,7 @@ public class MethodReference extends SpelNodeImpl {
} }
} }
MethodExecutor executorToUse = getCachedExecutor(argumentTypes); MethodExecutor executorToUse = getCachedExecutor(target, argumentTypes);
if (executorToUse != null) { if (executorToUse != null) {
try { try {
return executorToUse.execute(state.getEvaluationContext(), return executorToUse.execute(state.getEvaluationContext(),
...@@ -139,7 +140,7 @@ public class MethodReference extends SpelNodeImpl { ...@@ -139,7 +140,7 @@ public class MethodReference extends SpelNodeImpl {
// either there was no accessor or it no longer existed // either there was no accessor or it no longer existed
executorToUse = findAccessorForMethod(this.name, argumentTypes, state); executorToUse = findAccessorForMethod(this.name, argumentTypes, state);
this.cachedExecutor = new CachedMethodExecutor(executorToUse, argumentTypes); this.cachedExecutor = new CachedMethodExecutor(executorToUse, target, argumentTypes);
try { try {
return executorToUse.execute(state.getEvaluationContext(), return executorToUse.execute(state.getEvaluationContext(),
state.getActiveContextObject().getValue(), arguments); state.getActiveContextObject().getValue(), arguments);
...@@ -227,15 +228,15 @@ public class MethodReference extends SpelNodeImpl { ...@@ -227,15 +228,15 @@ public class MethodReference extends SpelNodeImpl {
: contextObject.getClass())); : contextObject.getClass()));
} }
private MethodExecutor getCachedExecutor(List<TypeDescriptor> argumentTypes) { private MethodExecutor getCachedExecutor(TypeDescriptor target,
if (this.cachedExecutor == null || !this.cachedExecutor.isSuitable(argumentTypes)) { List<TypeDescriptor> argumentTypes) {
if (this.cachedExecutor == null || !this.cachedExecutor.isSuitable(target, argumentTypes)) {
this.cachedExecutor = null; this.cachedExecutor = null;
return null; return null;
} }
return this.cachedExecutor.get(); return this.cachedExecutor.get();
} }
private class MethodValueRef implements ValueRef { private class MethodValueRef implements ValueRef {
private final ExpressionState state; private final ExpressionState state;
...@@ -244,15 +245,19 @@ public class MethodReference extends SpelNodeImpl { ...@@ -244,15 +245,19 @@ public class MethodReference extends SpelNodeImpl {
private final Object target; private final Object target;
private TypeDescriptor targetType;
private final Object[] arguments; private final Object[] arguments;
private List<TypeDescriptor> argumentTypes; private List<TypeDescriptor> argumentTypes;
MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, Object object, Object[] arguments) { MethodValueRef(ExpressionState state, EvaluationContext evaluationContext,
Object object, Object[] arguments) {
this.state = state; this.state = state;
this.evaluationContext = evaluationContext; this.evaluationContext = evaluationContext;
this.target = object; this.target = object;
this.targetType = TypeDescriptor.valueOf(target.getClass());
this.arguments = arguments; this.arguments = arguments;
this.argumentTypes = getTypes(this.arguments); this.argumentTypes = getTypes(this.arguments);
} }
...@@ -260,7 +265,8 @@ public class MethodReference extends SpelNodeImpl { ...@@ -260,7 +265,8 @@ public class MethodReference extends SpelNodeImpl {
@Override @Override
public TypedValue getValue() { public TypedValue getValue() {
MethodExecutor executorToUse = getCachedExecutor(this.argumentTypes); MethodExecutor executorToUse = getCachedExecutor(this.targetType,
this.argumentTypes);
if (executorToUse != null) { if (executorToUse != null) {
try { try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments); return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
...@@ -285,7 +291,7 @@ public class MethodReference extends SpelNodeImpl { ...@@ -285,7 +291,7 @@ public class MethodReference extends SpelNodeImpl {
// either there was no accessor or it no longer existed // either there was no accessor or it no longer existed
executorToUse = findAccessorForMethod(MethodReference.this.name, argumentTypes, this.target, this.evaluationContext); executorToUse = findAccessorForMethod(MethodReference.this.name, argumentTypes, this.target, this.evaluationContext);
MethodReference.this.cachedExecutor = new CachedMethodExecutor(executorToUse, this.argumentTypes); MethodReference.this.cachedExecutor = new CachedMethodExecutor(executorToUse, this.targetType, this.argumentTypes);
try { try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments); return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
} }
...@@ -310,23 +316,24 @@ public class MethodReference extends SpelNodeImpl { ...@@ -310,23 +316,24 @@ public class MethodReference extends SpelNodeImpl {
} }
} }
private static class CachedMethodExecutor { private static class CachedMethodExecutor {
private final MethodExecutor methodExecutor; private final MethodExecutor methodExecutor;
private final List<TypeDescriptor> argumentTypes; private final TypeDescriptor target;
private final List<TypeDescriptor> argumentTypes;
public CachedMethodExecutor(MethodExecutor methodExecutor, public CachedMethodExecutor(MethodExecutor methodExecutor, TypeDescriptor target,
List<TypeDescriptor> argumentTypes) { List<TypeDescriptor> argumentTypes) {
this.methodExecutor = methodExecutor; this.methodExecutor = methodExecutor;
this.target = target;
this.argumentTypes = argumentTypes; this.argumentTypes = argumentTypes;
} }
public boolean isSuitable(TypeDescriptor target, List<TypeDescriptor> argumentTypes) {
public boolean isSuitable(List<TypeDescriptor> argumentTypes) { return (this.methodExecutor != null && this.target != null
return (this.methodExecutor != null && this.argumentTypes.equals(argumentTypes)); && this.target.equals(target) && this.argumentTypes.equals(argumentTypes));
} }
public MethodExecutor get() { public MethodExecutor get() {
......
...@@ -45,8 +45,8 @@ public class CachedMethodExecutorTests { ...@@ -45,8 +45,8 @@ public class CachedMethodExecutorTests {
@Test @Test
public void testCachedExecution() throws Exception { public void testCachedExecutionForParameters() throws Exception {
Expression expression = this.parser.parseExpression("echo(#something)"); Expression expression = this.parser.parseExpression("echo(#var)");
assertMethodExecution(expression, 42, "int: 42"); assertMethodExecution(expression, 42, "int: 42");
assertMethodExecution(expression, 42, "int: 42"); assertMethodExecution(expression, 42, "int: 42");
...@@ -54,18 +54,32 @@ public class CachedMethodExecutorTests { ...@@ -54,18 +54,32 @@ public class CachedMethodExecutorTests {
assertMethodExecution(expression, 42, "int: 42"); assertMethodExecution(expression, 42, "int: 42");
} }
@Test
public void testCachedExecutionForTarget() throws Exception {
Expression expression = this.parser.parseExpression("#var.echo(42)");
assertMethodExecution(expression, new RootObject(), "int: 42");
assertMethodExecution(expression, new RootObject(), "int: 42");
assertMethodExecution(expression, new BaseObject(), "String: 42");
assertMethodExecution(expression, new RootObject(), "int: 42");
}
private void assertMethodExecution(Expression expression, Object var, String expected) { private void assertMethodExecution(Expression expression, Object var, String expected) {
this.context.setVariable("something", var); this.context.setVariable("var", var);
assertEquals(expected, expression.getValue(this.context)); assertEquals(expected, expression.getValue(this.context));
} }
public static class RootObject { public static class BaseObject {
public String echo(String value) { public String echo(String value) {
return "String: " + value; return "String: " + value;
} }
}
public static class RootObject extends BaseObject {
public String echo(int value) { public String echo(int value) {
return "int: " + value; return "int: " + value;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册