Ensure getters have non-void return types in SpEL

Closes gh-36800
This commit is contained in:
Sam Brannen
2026-05-04 18:41:17 +02:00
committed by Brian Clozel
parent 7a8917b137
commit 83667f808c
3 changed files with 22 additions and 2 deletions
@@ -374,10 +374,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
* Find a getter method for the specified property.
*/
protected @Nullable Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
Method method = findMethodForProperty(getPropertyMethodSuffixes(propertyName),
String[] methodSuffixes = getPropertyMethodSuffixes(propertyName);
Method method = findMethodForProperty(methodSuffixes,
"get", clazz, mustBeStatic, 0, ANY_TYPES);
if (method == null) {
method = findMethodForProperty(getPropertyMethodSuffixes(propertyName),
method = findMethodForProperty(methodSuffixes,
"is", clazz, mustBeStatic, 0, BOOLEAN_TYPES);
if (method == null) {
// Record-style plain accessor method, for example, name()
@@ -385,6 +386,9 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
"", clazz, mustBeStatic, 0, ANY_TYPES);
}
}
if (method != null && method.getReturnType() == void.class) {
method = null; // not a valid accessor method
}
return method;
}
@@ -16,6 +16,7 @@
package org.springframework.expression.spel;
import java.lang.reflect.Method;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
@@ -39,6 +40,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.Inventor;
import org.springframework.expression.spel.testresources.Person;
import org.springframework.expression.spel.testresources.RecordPerson;
import org.springframework.util.ClassUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -202,6 +204,16 @@ class PropertyAccessTests extends AbstractExpressionTests {
target.setName("p2");
assertThat(expr.getValue(context, target)).isEqualTo("p2");
assertThatSpelEvaluationException()
.isThrownBy(() -> parser.parseExpression("nonexistent").getValue(context, target))
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE);
Method getInvalid = ClassUtils.getMethod(Person.class, "getInvalid");
assertThat(getInvalid.getReturnType()).isEqualTo(void.class);
assertThatSpelEvaluationException()
.isThrownBy(() -> parser.parseExpression("invalid").getValue(context, target))
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE);
assertThatSpelEvaluationException()
.isThrownBy(() -> parser.parseExpression("name='p3'").getValue(context, target))
.extracting(SpelEvaluationException::getMessageCode).isEqualTo(SpelMessage.NOT_ASSIGNABLE);
@@ -59,4 +59,8 @@ public class Person {
return company;
}
public void getInvalid() {
// no-op
}
}