Skip to content

Fix property name resolution for record accessors#36911

Open
junhyeong9812 wants to merge 1 commit into
spring-projects:mainfrom
junhyeong9812:fix/property-resolvename-startswith
Open

Fix property name resolution for record accessors#36911
junhyeong9812 wants to merge 1 commit into
spring-projects:mainfrom
junhyeong9812:fix/property-resolvename-startswith

Conversation

@junhyeong9812

@junhyeong9812 junhyeong9812 commented Jun 13, 2026

Copy link
Copy Markdown

Overview

org.springframework.core.convert.Property#resolveName() derives a property
name from a read/write Method. It locates the get/is/set accessor prefix
with String#indexOf, which matches the prefix anywhere in the method name.

Problem

For record-style accessors (supported since gh-26029) whose component name
contains such a token, the wrong portion is stripped. Because is is a very
common bigram, this is not limited to contrived names — many ordinary record
component names are affected:

accessor current result expected
budget() / widget() / gadget() "" (empty) budget / widget / gadget
issue() / island() sue / land issue / island
history() / distance() tory / tance history / distance
decision() / visible() ion / ible decision / visible

An empty/incorrect name then makes Property#getField() fail to locate the
record component's backing field, so annotations declared on the component are
silently dropped — for example when a record property is accessed through SpEL's
ReflectivePropertyAccessor, which builds a Property without an explicit name.

Before record support the read method always started with get/is (otherwise
it threw), so indexOf behaved like startsWith. Record support added an
index = 0 fallback for plain accessors but did not account for a prefix
appearing elsewhere in the name.

Fix

Detect record component accessors first (the declaring class is a record and the
method is one of its record component accessors) and otherwise match the
get/is/set prefix only at the start of the method name via startsWith.
Regular JavaBeans accessors are unaffected.

The setter branch is likewise switched from indexOf("set") to
startsWith("set"), which is stricter for non-setter write methods passed to the
constructor (these now consistently throw IllegalArgumentException).

Tests

Adds PropertyTests covering standard getters, boolean getters and setters;
record accessors that embed (budget) or start with (issue) a prefix; plain
record accessors; a JavaBeans getter declared on a record; and record components
literally named get/is.

Property.resolveName() located the get/is/set accessor prefix with
String.indexOf, which matches the prefix anywhere in the method name.
A record component accessor whose name embeds such a prefix (for
example budget()) had the wrong portion stripped and resolved to an
empty or wrong property name, which in turn caused the component's
backing field annotations to be dropped.

Detect record component accessors first and otherwise match the
get/is/set prefix only at the start of the method name via startsWith.
Regular JavaBeans accessors are unaffected.

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jun 13, 2026
@sbrannen sbrannen added the in: core Issues in core modules (aop, beans, core, context, expression) label Jun 13, 2026
@sbrannen sbrannen self-assigned this Jun 13, 2026
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) status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants