Skip to content

Invoke methods via public interface/superclass in compiled SpEL expressions #29857

@drekbour

Description

@drekbour

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()));
}

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions