Skip to content

Commit 079ca80

Browse files
committed
SpEL supports record-style accessor methods as well
Closes gh-26029
1 parent 412aa06 commit 079ca80

File tree

5 files changed

+95
-37
lines changed

5 files changed

+95
-37
lines changed

spring-core/src/main/java/org/springframework/core/convert/Property.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -118,7 +118,7 @@ public Method getWriteMethod() {
118118
}
119119

120120

121-
// package private
121+
// Package private
122122

123123
MethodParameter getMethodParameter() {
124124
return this.methodParameter;
@@ -132,7 +132,7 @@ Annotation[] getAnnotations() {
132132
}
133133

134134

135-
// internal helpers
135+
// Internal helpers
136136

137137
private String resolveName() {
138138
if (this.readMethod != null) {
@@ -142,10 +142,13 @@ private String resolveName() {
142142
}
143143
else {
144144
index = this.readMethod.getName().indexOf("is");
145-
if (index == -1) {
146-
throw new IllegalArgumentException("Not a getter method");
145+
if (index != -1) {
146+
index += 2;
147+
}
148+
else {
149+
// Record-style plain accessor method, e.g. name()
150+
index = 0;
147151
}
148-
index += 2;
149152
}
150153
return StringUtils.uncapitalize(this.readMethod.getName().substring(index));
151154
}

spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,11 @@ protected Method findGetterForProperty(String propertyName, Class<?> clazz, bool
395395
if (method == null) {
396396
method = findMethodForProperty(getPropertyMethodSuffixes(propertyName),
397397
"is", clazz, mustBeStatic, 0, BOOLEAN_TYPES);
398+
if (method == null) {
399+
// Record-style plain accessor method, e.g. name()
400+
method = findMethodForProperty(new String[] {propertyName},
401+
"", clazz, mustBeStatic, 0, ANY_TYPES);
402+
}
398403
}
399404
return method;
400405
}
@@ -683,12 +688,11 @@ public boolean canRead(EvaluationContext context, @Nullable Object target, Strin
683688
return true;
684689
}
685690
getterName = "is" + StringUtils.capitalize(name);
686-
return getterName.equals(method.getName());
687-
}
688-
else {
689-
Field field = (Field) this.member;
690-
return field.getName().equals(name);
691+
if (getterName.equals(method.getName())) {
692+
return true;
693+
}
691694
}
695+
return this.member.getName().equals(name);
692696
}
693697

694698
@Override

spring-expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@
3636
import org.springframework.expression.spel.support.StandardEvaluationContext;
3737
import org.springframework.expression.spel.testresources.Inventor;
3838
import org.springframework.expression.spel.testresources.Person;
39+
import org.springframework.expression.spel.testresources.RecordPerson;
3940

4041
import static org.assertj.core.api.Assertions.assertThat;
4142
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -191,6 +192,20 @@ public void propertyReadOnly() {
191192
parser.parseExpression("name='p3'").getValue(context, target));
192193
}
193194

195+
@Test
196+
public void propertyReadOnlyWithRecordStyle() {
197+
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
198+
199+
Expression expr = parser.parseExpression("name");
200+
RecordPerson target1 = new RecordPerson("p1");
201+
assertThat(expr.getValue(context, target1)).isEqualTo("p1");
202+
RecordPerson target2 = new RecordPerson("p2");
203+
assertThat(expr.getValue(context, target2)).isEqualTo("p2");
204+
205+
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() ->
206+
parser.parseExpression("name='p3'").getValue(context, target2));
207+
}
208+
194209
@Test
195210
public void propertyReadWrite() {
196211
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

spring-expression/src/test/java/org/springframework/expression/spel/SpelCompilationCoverageTests.java

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4180,6 +4180,13 @@ public void propertyReference() throws Exception {
41804180
assertThat(expression.getValue(tc)).isEqualTo("value4");
41814181
assertCanCompile(expression);
41824182
assertThat(expression.getValue(tc)).isEqualTo("value4");
4183+
4184+
// record-style accessor
4185+
expression = parser.parseExpression("strawberry");
4186+
assertCantCompile(expression);
4187+
assertThat(expression.getValue(tc)).isEqualTo("value5");
4188+
assertCanCompile(expression);
4189+
assertThat(expression.getValue(tc)).isEqualTo("value5");
41834190
}
41844191

41854192
@Test
@@ -4553,23 +4560,9 @@ public void variantGetter() throws Exception {
45534560
Object v = expression.getValue(ctx,holder);
45544561
assertThat(v).isEqualTo("abc");
45554562

4556-
// // time it interpreted
4557-
// long stime = System.currentTimeMillis();
4558-
// for (int i = 0; i < 100000; i++) {
4559-
// v = expression.getValue(ctx,holder);
4560-
// }
4561-
// System.out.println((System.currentTimeMillis() - stime));
4562-
45634563
assertCanCompile(expression);
45644564
v = expression.getValue(ctx,holder);
45654565
assertThat(v).isEqualTo("abc");
4566-
4567-
// // time it compiled
4568-
// stime = System.currentTimeMillis();
4569-
// for (int i = 0; i < 100000; i++) {
4570-
// v = expression.getValue(ctx,holder);
4571-
// }
4572-
// System.out.println((System.currentTimeMillis() - stime));
45734566
}
45744567

45754568
@Test
@@ -4985,13 +4978,12 @@ private void verifyCompilationAndBehaviourWithNull(String expressionText, SpelEx
49854978
assertThat(fast.compileExpression()).isTrue();
49864979
r.setValue2(null);
49874980
// try the numbers 0,1,2,null
4988-
for (int i=0;i<4;i++) {
4989-
r.setValue(i<3?i:null);
4981+
for (int i = 0; i < 4; i++) {
4982+
r.setValue(i < 3 ? i : null);
49904983
boolean slowResult = (Boolean)slow.getValue(ctx);
49914984
boolean fastResult = (Boolean)fast.getValue(ctx);
4992-
// System.out.println("Trying "+expressionText+" with value="+r.getValue()+" result is "+slowResult);
4993-
assertThat(fastResult).as(" Differing results: expression="+expressionText+
4994-
" value="+r.getValue()+" slow="+slowResult+" fast="+fastResult).isEqualTo(slowResult);
4985+
assertThat(fastResult).as("Differing results: expression=" + expressionText +
4986+
" value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult);
49954987
}
49964988
}
49974989

@@ -5002,13 +4994,12 @@ private void verifyCompilationAndBehaviourWithNull2(String expressionText, SpelE
50024994
assertThat(fast.compileExpression()).isTrue();
50034995
Reg r = (Reg)ctx.getRootObject().getValue();
50044996
// try the numbers 0,1,2,null
5005-
for (int i=0;i<4;i++) {
5006-
r.setValue(i<3?i:null);
4997+
for (int i = 0; i < 4; i++) {
4998+
r.setValue(i < 3 ? i : null);
50074999
boolean slowResult = (Boolean)slow.getValue(ctx);
50085000
boolean fastResult = (Boolean)fast.getValue(ctx);
5009-
// System.out.println("Trying "+expressionText+" with value="+r.getValue()+" result is "+slowResult);
5010-
assertThat(fastResult).as(" Differing results: expression="+expressionText+
5011-
" value="+r.getValue()+" slow="+slowResult+" fast="+fastResult).isEqualTo(slowResult);
5001+
assertThat(fastResult).as("Differing results: expression=" + expressionText +
5002+
" value=" + r.getValue() + " slow=" + slowResult + " fast="+fastResult).isEqualTo(slowResult);
50125003
}
50135004
}
50145005

@@ -5839,7 +5830,6 @@ public static class TestClass6 {
58395830

58405831
public String orange = "value1";
58415832
public static String apple = "value2";
5842-
58435833
public long peach = 34L;
58445834

58455835
public String getBanana() {
@@ -5849,6 +5839,10 @@ public String getBanana() {
58495839
public static String getPlum() {
58505840
return "value4";
58515841
}
5842+
5843+
public String strawberry() {
5844+
return "value5";
5845+
}
58525846
}
58535847

58545848

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.expression.spel.testresources;
18+
19+
public class RecordPerson {
20+
21+
private String name;
22+
23+
private Company company;
24+
25+
public RecordPerson(String name) {
26+
this.name = name;
27+
}
28+
29+
public RecordPerson(String name, Company company) {
30+
this.name = name;
31+
this.company = company;
32+
}
33+
34+
public String name() {
35+
return name;
36+
}
37+
38+
public Company company() {
39+
return company;
40+
}
41+
42+
}

0 commit comments

Comments
 (0)