Browse Source

Fix regression in static setter method support

The intention of ExtendedBeanInfo, introduced with SPR-8079 in
v3.1.0.M2, was to support dependency injection against non-void
returning write methods. However, it also inadvertently introduced
support for injection against static setter methods.

When use of ExtendedBeanInfo was made optional with SPR-9723 in
v3.2.0.M2, ExtendedBeanInfo continued to support static write methods,
but its new BeanInfoFactory-based approach to testing whether or not
a given bean class contains candidate write methods was written in a
fashion exclusive of static methods, and this thereby introduced a
regression - a regression in an otherwise undocumented and unintended
feature, but a regression nevertheless.

The reporting of SPR-10115 proves that at least one user has come to
depend on this behavior allowing injection against static write
methods, and so this commit fixes the regression by ensuring that the
candidacy test includes standard and non-void setter methods having a
static modifier.

Issue: SPR-10115, SPR-9723, SPR-8079
pull/213/head
Chris Beams 12 years ago
parent
commit
7a19fd5750
  1. 21
      spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
  2. 2
      spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java
  3. 18
      spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
  4. 24
      spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java

21
spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java

@ -41,8 +41,8 @@ import static org.springframework.beans.PropertyDescriptorUtils.*; @@ -41,8 +41,8 @@ import static org.springframework.beans.PropertyDescriptorUtils.*;
/**
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register non-void
* returning setter methods. For example:
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
* and/or non-void returning setter methods. For example:
* <pre>{@code
* public class Bean {
* private Foo foo;
@ -102,37 +102,40 @@ class ExtendedBeanInfo implements BeanInfo { @@ -102,37 +102,40 @@ class ExtendedBeanInfo implements BeanInfo {
new SimpleNonIndexedPropertyDescriptor(pd));
}
for (Method method : findNonVoidWriteMethods(delegate.getMethodDescriptors())) {
handleNonVoidWriteMethod(method);
for (Method method : findCandidateWriteMethods(delegate.getMethodDescriptors())) {
handleCandidateWriteMethod(method);
}
}
private List<Method> findNonVoidWriteMethods(MethodDescriptor[] methodDescriptors) {
private List<Method> findCandidateWriteMethods(MethodDescriptor[] methodDescriptors) {
List<Method> matches = new ArrayList<Method>();
for (MethodDescriptor methodDescriptor : methodDescriptors) {
Method method = methodDescriptor.getMethod();
if (isNonVoidWriteMethod(method)) {
if (isCandidateWriteMethod(method)) {
matches.add(method);
}
}
return matches;
}
public static boolean isNonVoidWriteMethod(Method method) {
public static boolean isCandidateWriteMethod(Method method) {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
int nParams = parameterTypes.length;
if (methodName.length() > 3 && methodName.startsWith("set") &&
Modifier.isPublic(method.getModifiers()) &&
!void.class.isAssignableFrom(method.getReturnType()) &&
(
!void.class.isAssignableFrom(method.getReturnType()) ||
Modifier.isStatic(method.getModifiers())
) &&
(nParams == 1 || (nParams == 2 && parameterTypes[0].equals(int.class)))) {
return true;
}
return false;
}
private void handleNonVoidWriteMethod(Method method) throws IntrospectionException {
private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
int nParams = method.getParameterTypes().length;
String propertyName = propertyNameFor(method);
Class<?> propertyType = method.getParameterTypes()[nParams-1];

2
spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java

@ -51,7 +51,7 @@ public class ExtendedBeanInfoFactory implements Ordered, BeanInfoFactory { @@ -51,7 +51,7 @@ public class ExtendedBeanInfoFactory implements Ordered, BeanInfoFactory {
*/
private boolean supports(Class<?> beanClass) {
for (Method method : beanClass.getMethods()) {
if (ExtendedBeanInfo.isNonVoidWriteMethod(method)) {
if (ExtendedBeanInfo.isCandidateWriteMethod(method)) {
return true;
}
}

18
spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java

@ -1550,6 +1550,24 @@ public final class BeanWrapperTests { @@ -1550,6 +1550,24 @@ public final class BeanWrapperTests {
assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
}
@Test
public void cornerSpr10115() {
Spr10115Bean foo = new Spr10115Bean();
BeanWrapperImpl bwi = new BeanWrapperImpl();
bwi.setWrappedInstance(foo);
bwi.setPropertyValue("prop1", "val1");
assertEquals("val1", Spr10115Bean.prop1);
}
static class Spr10115Bean {
private static String prop1;
public static void setProp1(String prop1) {
Spr10115Bean.prop1 = prop1;
}
}
private static class Foo {

24
spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java

@ -946,4 +946,28 @@ public class ExtendedBeanInfoTests { @@ -946,4 +946,28 @@ public class ExtendedBeanInfoTests {
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
}
}
@Test
public void shouldSupportStaticWriteMethod() throws IntrospectionException {
{
BeanInfo bi = Introspector.getBeanInfo(WithStaticWriteMethod.class);
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
}
{
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(WithStaticWriteMethod.class));
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(true));
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
}
}
static class WithStaticWriteMethod {
@SuppressWarnings("unused")
public static void setProp1(String prop1) {
}
}
}

Loading…
Cancel
Save