-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
Affects: 6.0.x
Many categories of basic SpelExpression are not compiled into a performant form due to a pattern of not acknowledging a Method can be called from superclass or interface. Best described with two simple failing tests for two scenarios. I have raised the issue as this feels like something resolvable.
ITEM1 MethodReference.isCompilable
SpelExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
@Test
public void basic_expressions_are_compiled() {
SpelExpression exp = (SpelExpression)parser.parseExpression("{2021, 2022}.contains(year)");
assert exp.getValue(context, LocalDate.parse("2022-01-19")) == true;
assert exp.compileExpression(); // FAIL - not compiled
}In this case it is the contains ...
org.springframework.expression.spel.ast.MethodReference#isCompilable
... checks concrete class and fails because it is not public...
public boolean java.util.Collections$UnmodifiableCollection.contains(java.lang.Object)
... even though MethodExecutor is already aware that the 'methodToInvoke' will be against an interface
public abstract boolean java.util.Collection.contains(java.lang.Object)
If the execution is on the interface, the check must be too.
Note: Similar expressions on properties (not methods) compile just fine (e.g. {2021, 2022}.size)
ITEM2 OptimalPropertyAccessor.isCompilable
However OptimalPropertyAccessor doesn't seem to understand overridden methods of parent classes (not interfaces which works fine as above).
public static class Y { public int getI() {return 1;} }
private static class Z extends Y { public int getI() {return 2;} }
@Test
public void basic_expressions_are_compiled() {
SpelExpression exp = (SpelExpression)parser.parseExpression("i");
Y y = new Z();
assert exp.getValue(context, y) == 2;
assert exp.compileExpression(); // FAIL - not compiled
}This is because OptimalPropertyAccessor.isCompilable() relies on Method.getDeclaringClass() which has this behaviour
public boolean isCompilable() {
return (Modifier.isPublic(this.member.getModifiers()) &&
Modifier.isPublic(this.member.getDeclaringClass().getModifiers()));
}