Skip to content

Commit 3af8a32

Browse files
committed
Correctly resolve accessors for static properties on Class
Issue: SPR-11609
1 parent e303536 commit 3af8a32

File tree

2 files changed

+66
-57
lines changed

2 files changed

+66
-57
lines changed

spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,7 @@ private TypedValue readProperty(TypedValue contextObject, EvaluationContext eCon
186186
}
187187
}
188188

189-
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
190-
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
191-
189+
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
192190
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
193191
// get the accessor and use it. If they are not cacheable but report they can read the property
194192
// then ask them to read it
@@ -214,7 +212,7 @@ private TypedValue readProperty(TypedValue contextObject, EvaluationContext eCon
214212
}
215213
else {
216214
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name,
217-
FormatHelper.formatClassNameForMessage(contextObjectClass));
215+
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
218216
}
219217
}
220218

@@ -236,9 +234,7 @@ private void writeProperty(TypedValue contextObject, EvaluationContext eContext,
236234
}
237235
}
238236

239-
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
240-
241-
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, eContext.getPropertyAccessors());
237+
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
242238
if (accessorsToTry != null) {
243239
try {
244240
for (PropertyAccessor accessor : accessorsToTry) {
@@ -259,18 +255,16 @@ private void writeProperty(TypedValue contextObject, EvaluationContext eContext,
259255
}
260256
else {
261257
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
262-
FormatHelper.formatClassNameForMessage(contextObjectClass));
258+
FormatHelper.formatClassNameForMessage(getObjectClass(contextObject.getValue())));
263259
}
264260
}
265261

266262
public boolean isWritableProperty(String name, TypedValue contextObject, EvaluationContext eContext) throws SpelEvaluationException {
267-
Object contextObjectValue = contextObject.getValue();
268-
// TypeDescriptor td = state.getActiveContextObject().getTypeDescriptor();
269-
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObjectValue), eContext.getPropertyAccessors());
270-
if (resolversToTry != null) {
271-
for (PropertyAccessor pfResolver : resolversToTry) {
263+
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObject.getValue(), eContext.getPropertyAccessors());
264+
if (accessorsToTry != null) {
265+
for (PropertyAccessor accessor : accessorsToTry) {
272266
try {
273-
if (pfResolver.canWrite(eContext, contextObjectValue, name)) {
267+
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
274268
return true;
275269
}
276270
}
@@ -290,27 +284,28 @@ public boolean isWritableProperty(String name, TypedValue contextObject, Evaluat
290284
* the start of the list. In addition, there are specific resolvers that exactly name the class in question and
291285
* resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
292286
* specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
293-
* @param targetType the type upon which property access is being attempted
287+
* @param contextObject the object upon which property access is being attempted
294288
* @return a list of resolvers that should be tried in order to access the property
295289
*/
296-
private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, List<PropertyAccessor> propertyAccessors) {
290+
private List<PropertyAccessor> getPropertyAccessorsToTry(Object contextObject, List<PropertyAccessor> propertyAccessors) {
291+
Class<?> targetType = (contextObject != null ? contextObject.getClass() : null);
292+
297293
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
298294
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
299295
for (PropertyAccessor resolver : propertyAccessors) {
300296
Class<?>[] targets = resolver.getSpecificTargetClasses();
301-
if (targets == null) { // generic resolver that says it can be used for any type
297+
if (targets == null) {
298+
// generic resolver that says it can be used for any type
302299
generalAccessors.add(resolver);
303300
}
304-
else {
305-
if (targetType != null) {
306-
for (Class<?> clazz : targets) {
307-
if (clazz == targetType) {
308-
specificAccessors.add( resolver);
309-
break;
310-
}
311-
else if (clazz.isAssignableFrom(targetType)) {
312-
generalAccessors.add(resolver);
313-
}
301+
else if (targetType != null) {
302+
for (Class<?> clazz : targets) {
303+
if (clazz == targetType) {
304+
specificAccessors.add(resolver);
305+
break;
306+
}
307+
else if (clazz.isAssignableFrom(targetType)) {
308+
generalAccessors.add(resolver);
314309
}
315310
}
316311
}

spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ static class TestProperties {
286286

287287
static class MapAccessor implements PropertyAccessor {
288288

289+
@Override
290+
public Class<?>[] getSpecificTargetClasses() {
291+
return new Class<?>[] {Map.class};
292+
}
293+
289294
@Override
290295
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
291296
return (((Map<?, ?>) target).containsKey(name));
@@ -306,11 +311,6 @@ public boolean canWrite(EvaluationContext context, Object target, String name) t
306311
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
307312
((Map<String, Object>) target).put(name, newValue);
308313
}
309-
310-
@Override
311-
public Class<?>[] getSpecificTargetClasses() {
312-
return new Class[] { Map.class };
313-
}
314314
}
315315

316316

@@ -1774,18 +1774,18 @@ public TypedValue execute(EvaluationContext context, Object target, Object... ar
17741774
public void SPR10486() throws Exception {
17751775
SpelExpressionParser parser = new SpelExpressionParser();
17761776
StandardEvaluationContext context = new StandardEvaluationContext();
1777-
SPR10486 rootObject = new SPR10486();
1777+
Spr10486 rootObject = new Spr10486();
17781778
Expression classNameExpression = parser.parseExpression("class.name");
17791779
Expression nameExpression = parser.parseExpression("name");
1780-
assertThat(classNameExpression.getValue(context, rootObject), equalTo((Object) SPR10486.class.getName()));
1780+
assertThat(classNameExpression.getValue(context, rootObject), equalTo((Object) Spr10486.class.getName()));
17811781
assertThat(nameExpression.getValue(context, rootObject), equalTo((Object) "name"));
17821782
}
17831783

17841784
@Test
17851785
public void SPR11142() throws Exception {
17861786
SpelExpressionParser parser = new SpelExpressionParser();
17871787
StandardEvaluationContext context = new StandardEvaluationContext();
1788-
SPR11142 rootObject = new SPR11142();
1788+
Spr11142 rootObject = new Spr11142();
17891789
Expression expression = parser.parseExpression("something");
17901790
thrown.expect(SpelEvaluationException.class);
17911791
thrown.expectMessage("'something' cannot be found");
@@ -1837,33 +1837,22 @@ public void SPR11445_beanReference() {
18371837
assertEquals(1, expr.getValue(context));
18381838
}
18391839

1840-
1841-
static class Spr11445Class implements BeanResolver {
1842-
1843-
private final AtomicInteger counter = new AtomicInteger();
1844-
1845-
public int echo(int invocation) {
1846-
return invocation;
1847-
}
1848-
1849-
public int parameter() {
1850-
return counter.incrementAndGet();
1851-
}
1852-
1853-
@Override
1854-
public Object resolve(EvaluationContext context, String beanName) throws AccessException {
1855-
return beanName.equals("bean") ? this : null;
1856-
}
1857-
}
1858-
1859-
18601840
@Test
18611841
public void SPR11494() {
18621842
Expression exp = new SpelExpressionParser().parseExpression("T(java.util.Arrays).asList('a','b')");
18631843
List<String> list = (List<String>) exp.getValue();
18641844
assertThat(list.size(), is(2));
18651845
}
18661846

1847+
@Test
1848+
public void SPR11609() {
1849+
StandardEvaluationContext sec = new StandardEvaluationContext();
1850+
sec.addPropertyAccessor(new MapAccessor());
1851+
Expression exp = new SpelExpressionParser().parseExpression(
1852+
"T(org.springframework.expression.spel.SpelReproTests$MapWithConstant).X");
1853+
assertEquals(1, exp.getValue(sec));
1854+
}
1855+
18671856

18681857
private static enum ABC { A, B, C }
18691858

@@ -1939,7 +1928,7 @@ public static class StaticFinalImpl2 extends AbstractStaticFinal {
19391928
}
19401929

19411930

1942-
public static class SPR10486 {
1931+
public static class Spr10486 {
19431932

19441933
private String name = "name";
19451934

@@ -1953,7 +1942,7 @@ public void setName(String name) {
19531942
}
19541943

19551944

1956-
static class SPR11142 {
1945+
static class Spr11142 {
19571946

19581947
public String isSomething() {
19591948
return "";
@@ -1983,4 +1972,29 @@ public boolean equals(Object o) {
19831972
}
19841973
}
19851974

1975+
1976+
static class Spr11445Class implements BeanResolver {
1977+
1978+
private final AtomicInteger counter = new AtomicInteger();
1979+
1980+
public int echo(int invocation) {
1981+
return invocation;
1982+
}
1983+
1984+
public int parameter() {
1985+
return counter.incrementAndGet();
1986+
}
1987+
1988+
@Override
1989+
public Object resolve(EvaluationContext context, String beanName) throws AccessException {
1990+
return beanName.equals("bean") ? this : null;
1991+
}
1992+
}
1993+
1994+
1995+
public static class MapWithConstant extends HashMap {
1996+
1997+
public static final int X = 1;
1998+
}
1999+
19862000
}

0 commit comments

Comments
 (0)