PropertyOrFieldReference checks cached PropertyAccessor against current EvaluationContext

Issue: SPR-15769
This commit is contained in:
Juergen Hoeller 2017-07-14 15:18:05 +02:00
parent 57f961e36b
commit bcf9f21ecc
2 changed files with 71 additions and 18 deletions

View File

@ -172,15 +172,17 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
PropertyAccessor accessorToUse = this.cachedReadAccessor; PropertyAccessor accessorToUse = this.cachedReadAccessor;
if (accessorToUse != null) { if (accessorToUse != null) {
if (evalContext.getPropertyAccessors().contains(accessorToUse)) {
try { try {
return accessorToUse.read(evalContext, contextObject.getValue(), name); return accessorToUse.read(evalContext, contextObject.getValue(), name);
} }
catch (Exception ex) { catch (Exception ex) {
// This is OK - it may have gone stale due to a class change, // This is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up... // let's try to get a new one and call it before giving up...
this.cachedReadAccessor = null;
} }
} }
this.cachedReadAccessor = null;
}
List<PropertyAccessor> accessorsToTry = List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors()); getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());
@ -225,6 +227,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
PropertyAccessor accessorToUse = this.cachedWriteAccessor; PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) { if (accessorToUse != null) {
if (evalContext.getPropertyAccessors().contains(accessorToUse)) {
try { try {
accessorToUse.write(evalContext, contextObject.getValue(), name, newValue); accessorToUse.write(evalContext, contextObject.getValue(), name, newValue);
return; return;
@ -232,9 +235,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
catch (Exception ex) { catch (Exception ex) {
// This is OK - it may have gone stale due to a class change, // This is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up... // let's try to get a new one and call it before giving up...
this.cachedWriteAccessor = null;
} }
} }
this.cachedWriteAccessor = null;
}
List<PropertyAccessor> accessorsToTry = List<PropertyAccessor> accessorsToTry =
getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors()); getPropertyAccessorsToTry(contextObject.getValue(), evalContext.getPropertyAccessors());

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,7 +17,9 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import org.junit.Test; import org.junit.Test;
@ -34,12 +36,11 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import static org.junit.Assert.*; import static org.junit.Assert.*;
///CLOVER:OFF
/** /**
* Tests accessing of properties. * Tests accessing of properties.
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller
*/ */
public class PropertyAccessTests extends AbstractExpressionTests { public class PropertyAccessTests extends AbstractExpressionTests {
@ -167,6 +168,20 @@ public class PropertyAccessTests extends AbstractExpressionTests {
assertEquals(value, "java.lang.String"); assertEquals(value, "java.lang.String");
} }
@Test
public void shouldAlwaysUsePropertyAccessorFromEvaluationContext() {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("name");
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new ConfigurablePropertyAccessor(Collections.singletonMap("name", "Ollie")));
assertEquals("Ollie", expression.getValue(context));
context = new StandardEvaluationContext();
context.addPropertyAccessor(new ConfigurablePropertyAccessor(Collections.singletonMap("name", "Jens")));
assertEquals("Jens", expression.getValue(context));
}
// This can resolve the property 'flibbles' on any String (very useful...) // This can resolve the property 'flibbles' on any String (very useful...)
private static class StringyPropertyAccessor implements PropertyAccessor { private static class StringyPropertyAccessor implements PropertyAccessor {
@ -216,4 +231,38 @@ public class PropertyAccessTests extends AbstractExpressionTests {
} }
} }
private static class ConfigurablePropertyAccessor implements PropertyAccessor {
private final Map<String, Object> values;
public ConfigurablePropertyAccessor(Map<String, Object> values) {
this.values = values;
}
@Override
public Class<?>[] getSpecificTargetClasses() {
return null;
}
@Override
public boolean canRead(EvaluationContext context, Object target, String name) {
return true;
}
@Override
public TypedValue read(EvaluationContext context, Object target, String name) {
return new TypedValue(this.values.get(name));
}
@Override
public boolean canWrite(EvaluationContext context, Object target, String name) {
return false;
}
@Override
public void write(EvaluationContext context, Object target, String name, Object newValue) {
}
}
} }