Skip to content

PropertyOrFieldReference invalidly reuses cached PropertyAccessor [SPR-15769] #20324

Closed
@spring-projects-issues

Description

@spring-projects-issues

Oliver Drotbohm opened SPR-15769 and commented

A PropertyOrFieldReference caches the PropertyAccessor used for the first evaluation of the node. However, if a new EvaluationContext is provided and a completely different set of PropertyAccessor instances is used, none of them actually ever get considered.

The test case below uses a custom PropertyAccessor that holds a Map of values. The second part of the test registers a new instance of it with different values and still the evaluation returns the old value.

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.util.Collections;
import java.util.Map;

import org.junit.Test;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

/**
 * @author Oliver Gierke
 */
public class PropertyOrFieldReferenceCachingIssue {

	@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")));

		assertThat(expression.getValue(context), is("Ollie"));

		context = new StandardEvaluationContext();
		context.addPropertyAccessor(new ConfigurablePropertyAccessor(Collections.singletonMap("name", "Jens")));

		assertThat(expression.getValue(context), is("Jens"));
	}

	static class ConfigurablePropertyAccessor implements PropertyAccessor {

		private final Map<String, Object> values;

		public ConfigurablePropertyAccessor(Map<String, Object> values) {
			this.values = values;
		}

		@Override
		public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
			return true;
		}

		@Override
		public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
			return false;
		}

		@Override
		public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {}

		@Override
		public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
			return new TypedValue(values.get(name));
		}

		@Override
		public Class<?>[] getSpecificTargetClasses() {
			return null;
		}
	}
}

Affects: 4.3.9, 5.0 RC2

Issue Links:

Referenced from: commits bcf9f21, bffcd33

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions