Fix SpEL JavaBean compliance

Before this fix ReflectivePropertyAccessor was not able to find write
method for valid property name that has first character in lower case
and second character in upper case. Write method lookup would always
convert first character of property name to upper case which is not
compliant with JavaBean specification. As consequence this caused an
unwanted behavior in SpEL when obtaining values of expressions that
reference such JavaBean properties.

As of this change, write method lookup skips conversion of property
name first character to upper case when property name is longer than
one character and second character is in upper case. This also fixes
mentioned bug in SpEL value resolution.

Issue: SPR-9123
master
Stevo Slavic 13 years ago committed by Chris Beams
parent 2503b7eb89
commit 1f28bdfbfa
  1. 14
      spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
  2. 21
      spring-expression/src/test/java/org/springframework/expression/spel/support/ReflectionHelperTests.java

@ -282,13 +282,19 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
/**
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
* 'get' and the rest of the name is the same as the property name (with the first character uppercased).
* Find a getter method for the specified property.
*/
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
Method[] ms = clazz.getMethods();
String propertyWriteMethodSuffix;
if (propertyName.length() > 1 && Character.isUpperCase(propertyName.charAt(1))) {
propertyWriteMethodSuffix = propertyName;
}
else {
propertyWriteMethodSuffix = StringUtils.capitalize(propertyName);
}
// Try "get*" method...
String getterName = "get" + StringUtils.capitalize(propertyName);
String getterName = "get" + propertyWriteMethodSuffix;
for (Method method : ms) {
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
@ -296,7 +302,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
}
// Try "is*" method...
getterName = "is" + StringUtils.capitalize(propertyName);
getterName = "is" + propertyWriteMethodSuffix;
for (Method method : ms) {
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
boolean.class.equals(method.getReturnType()) &&

@ -318,7 +318,17 @@ public class ReflectionHelperTests extends ExpressionTestCase {
// Assert.assertEquals(0,rpr.read(ctx,t,"field3").getValue());
Assert.assertEquals(false,rpr.read(ctx,t,"property4").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"property4"));
// repro SPR-9123, ReflectivePropertyAccessor JavaBean property names compliance tests
Assert.assertEquals("iD",rpr.read(ctx,t,"iD").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"iD"));
Assert.assertEquals("id",rpr.read(ctx,t,"id").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"id"));
Assert.assertEquals("ID",rpr.read(ctx,t,"ID").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"ID"));
// note: "Id" is not a valid JavaBean name, nevertheless it is treated as "id"
Assert.assertEquals("id",rpr.read(ctx,t,"Id").getValue());
Assert.assertTrue(rpr.canRead(ctx,t,"Id"));
}
@Test
@ -406,6 +416,9 @@ public class ReflectionHelperTests extends ExpressionTestCase {
String property2;
String property3 = "doodoo";
boolean property4 = false;
String iD = "iD";
String id = "id";
String ID = "ID";
public String getProperty() { return property; }
public void setProperty(String value) { property = value; }
@ -415,6 +428,12 @@ public class ReflectionHelperTests extends ExpressionTestCase {
public String getProperty3() { return property3; }
public boolean isProperty4() { return property4; }
public String getiD() { return iD; }
public String getId() { return id; }
public String getID() { return ID; }
}
static class Super {

Loading…
Cancel
Save