Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpEL PropertyAccessor ordering for supertype and generic matches does not adhere to contract #33861

Closed
4 tasks done
sbrannen opened this issue Nov 8, 2024 · 1 comment
Closed
4 tasks done
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@sbrannen
Copy link
Member

sbrannen commented Nov 8, 2024

Overview

The PropertyAccessor SPI in the Spring Expression Language (SpEL) defines a getSpecificTargetClasses() method which allows an accessor to declare what types the accessor supports, or null if it is a "generic" accessor.

The contract for ordering accessors is only partially mentioned in the Javadoc for PropertyAccessor; however, it is explained in detail in the Javadoc for org.springframework.expression.spel.ast.AstUtils.getPropertyAccessorsToTry():

The resolvers are considered to be in an ordered list, however in the returned list any that are exact matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at the start of the list. In addition, there are specific resolvers that exactly name the class in question and resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the specific resolvers set and will be tried after exactly matching accessors but before generic accessors.

And similar Javadoc exists for the private getPropertyAccessorsToTry() method in PropertyOrFieldReference.

However, the implementations of AstUtils.getPropertyAccessorsToTry() and PropertyOrFieldReference.getPropertyAccessorsToTry() do not honor that last part of the contract.

On the contrary, a generic accessor (such as ReflectivePropertyAccessor) is ordered before a custom PropertyAccessor that claims to support a supertype of the target type, if the generic accessor is registered before the custom accessor.

In other words, a generic accessor can incorrectly take priority over a matching type-specific accessor.

Deliverables

  • Ensure that property accessors are ordered so that type-matching accessors always have a higher priority than generic/fallback accessors.
  • Ensure that property accessors are evaluated in the order in which they were registered.
  • Fix the algorithm in AstUtils.getPropertyAccessorsToTry() and remove the duplicate algorithm in PropertyOrFieldReference.getPropertyAccessorsToTry().
  • Introduce tests for AstUtils.getPropertyAccessorsToTry() in 6.1.x and main.

Related Issues

@sbrannen sbrannen added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) labels Nov 8, 2024
@sbrannen sbrannen added this to the 6.2.0-M1 milestone Nov 8, 2024
@sbrannen sbrannen self-assigned this Nov 8, 2024
sbrannen added a commit that referenced this issue Nov 8, 2024
@sbrannen
Copy link
Member Author

sbrannen commented Nov 8, 2024

This issue was fixed in Spring Framework 6.2 M1 in commits 4b0a048 and d912770 and tested in Spring Framework 6.1 in commit 9724f9b and in Spring Framework 6.2 in commit b60cc54.

sbrannen added a commit to sbrannen/spring-webflow that referenced this issue Nov 9, 2024
Prior to this commit, if a Spring Expression Language (SpEL) expression
did not include parentheses for a MultiAction method that does not
accept a RequestContext argument, the flow would pass on Spring
Framework versions prior to 6.2, but the same flow would fail on Spring
Framework 6.2 M1 or later. The following is such an expression and is
effectively a property reference which must be handled by a registered
PropertyAccessor: "reportActions.evaluateReport".

The reason for the change in behavior is that Spring Framework 6.2 M1
includes a bug fix for ordering SpEL PropertyAccessors. That bug fix
correctly results in Spring Web Flow's ActionPropertyAccessor being
evaluated before SpEL's ReflectivePropertyAccessor. Consequently, an
expression such as "reportActions.evaluateReport" is now handled by
ActionPropertyAccessor which indirectly requires that the action method
accept a RequestContext argument. When the method does not accept a
RequestContext argument, the flow fails with a NoSuchMethodException.

To address that, this commit revises the implementation of canRead(...)
in ActionPropertyAccessor so that it only returns `true` if the action
method accepts a RequestContext argument. When ActionPropertyAccessor's
canRead(...) method returns `false`, the generic
ReflectivePropertyAccessor will be used instead, which restores the
pre-6.2 behavior for such expressions.

The test suite passes for the following Spring Framework versions.

- ./gradlew -PspringFrameworkVersion=6.0.23 --rerun-tasks clean check
- ./gradlew -PspringFrameworkVersion=6.1.14 --rerun-tasks clean check
- ./gradlew -PspringFrameworkVersion=6.2.0-RC3 --rerun-tasks clean check

See spring-projects/spring-framework#33861
See spring-projects/spring-framework#33862
Closes spring-projectsgh-1802
bclozel pushed a commit that referenced this issue Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

1 participant