Support SpEL compilation for public methods in private subtypes

Commit c79436f832 ensured that methods are invoked via a public
interface or public superclass when compiling Spring Expression
Language (SpEL) expressions involving method references or property
access (see MethodReference, PropertyOrFieldReference, and
collaborating support classes). However, compilation of expressions
that access properties by indexing into an object by property name is
still not properly supported in all scenarios.

To address those remaining use cases, this commit ensures that methods
are invoked via a public interface or public superclass when accessing
a property by indexing into an object by the property name – for
example, `person['name']` instead of `person.name`.

In addition, SpEL's Indexer now properly relies on the
CompilablePropertyAccessor abstraction instead of hard-coding support
for only OptimalPropertyAccessor. This greatly reduces the complexity
of the Indexer and simultaneously allows the Indexer to potentially
support other CompilablePropertyAccessor implementations.

Closes gh-29857
This commit is contained in:
Sam Brannen
2024-03-09 13:49:10 +01:00
parent 107f47cfcf
commit 65d77624d1
4 changed files with 68 additions and 40 deletions
@@ -764,6 +764,54 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
assertThat(result).isEqualTo(2);
}
@Test
void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicInterface() {
expression = parser.parseExpression("#root['text']");
PrivateSubclass privateSubclass = new PrivateSubclass();
// Prerequisite: type must not be public for this use case.
assertNotPublic(privateSubclass.getClass());
String result = expression.getValue(context, privateSubclass, String.class);
assertThat(result).isEqualTo("enigma");
assertCanCompile(expression);
result = expression.getValue(context, privateSubclass, String.class);
assertThat(result).isEqualTo("enigma");
}
@Test
void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPrivateInterface() {
expression = parser.parseExpression("#root['message']");
PrivateSubclass privateSubclass = new PrivateSubclass();
// Prerequisite: type must not be public for this use case.
assertNotPublic(privateSubclass.getClass());
String result = expression.getValue(context, privateSubclass, String.class);
assertThat(result).isEqualTo("hello");
assertCanCompile(expression);
result = expression.getValue(context, privateSubclass, String.class);
assertThat(result).isEqualTo("hello");
}
@Test
void indexIntoPropertyInPrivateSubclassThatOverridesPropertyInPublicSuperclass() {
expression = parser.parseExpression("#root['number']");
PrivateSubclass privateSubclass = new PrivateSubclass();
// Prerequisite: type must not be public for this use case.
assertNotPublic(privateSubclass.getClass());
Integer result = expression.getValue(context, privateSubclass, Integer.class);
assertThat(result).isEqualTo(2);
assertCanCompile(expression);
result = expression.getValue(context, privateSubclass, Integer.class);
assertThat(result).isEqualTo(2);
}
private interface PrivateInterface {
String getMessage();