diff --git a/spring-core/src/main/java/org/springframework/core/convert/Property.java b/spring-core/src/main/java/org/springframework/core/convert/Property.java index 2967fe46ef7..1be7788b9ee 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/Property.java +++ b/spring-core/src/main/java/org/springframework/core/convert/Property.java @@ -153,12 +153,11 @@ public final class Property { return StringUtils.uncapitalize(methodName.substring(index)); } else if (this.writeMethod != null) { - int index = this.writeMethod.getName().indexOf("set"); - if (index == -1) { + String methodName = this.writeMethod.getName(); + if (!methodName.startsWith("set")) { throw new IllegalArgumentException("Not a setter method"); } - index += 3; - return StringUtils.uncapitalize(this.writeMethod.getName().substring(index)); + return StringUtils.uncapitalize(methodName.substring(3)); } else { throw new IllegalStateException("Property is neither readable nor writable"); diff --git a/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java b/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java index bc89545630c..0039faec473 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/PropertyTests.java @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** * Tests for {@link Property} name resolution. @@ -47,8 +48,28 @@ class PropertyTests { @Test void resolveNameForSetter() throws Exception { - Method setter = TestBean.class.getMethod("setName", String.class); - assertThat(new Property(TestBean.class, null, setter).getName()).isEqualTo("name"); + assertThat(writeProperty("setName").getName()).isEqualTo("name"); + } + + @Test // no "set" token at all: rejected before and after this change + void rejectNonSetterWriteMethod() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("updateName")) + .withMessage("Not a setter method"); + } + + @Test // "set" embedded mid-name: formerly accepted and resolved to "x" + void rejectWriteMethodEmbeddingSetInName() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("offsetX")) + .withMessage("Not a setter method"); + } + + @Test // "set" at the end of the name: formerly accepted and resolved to "" + void rejectWriteMethodEndingWithSetToken() { + assertThatIllegalArgumentException() + .isThrownBy(() -> writeProperty("upset")) + .withMessage("Not a setter method"); } @Test // record component accessor whose name embeds the "get" prefix @@ -127,6 +148,11 @@ class PropertyTests { return new Property(objectType, readMethod, null); } + private static Property writeProperty(String writeMethodName) throws Exception { + Method writeMethod = TestBean.class.getMethod(writeMethodName, String.class); + return new Property(TestBean.class, null, writeMethod); + } + @SuppressWarnings("unused") static class TestBean { @@ -145,6 +171,15 @@ class PropertyTests { public void setName(String name) { } + + public void updateName(String name) { + } + + public void offsetX(String value) { + } + + public void upset(String value) { + } } record SampleRecord(String name, String budget, String issue) {