From 7a19fd575045333b970e645f6db8a15302484038 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 31 Dec 2012 14:16:38 +0100 Subject: [PATCH] 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 --- .../beans/ExtendedBeanInfo.java | 21 +++++++++------- .../beans/ExtendedBeanInfoFactory.java | 2 +- .../beans/BeanWrapperTests.java | 18 ++++++++++++++ .../beans/ExtendedBeanInfoTests.java | 24 +++++++++++++++++++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java index 76c295490e..54467cd3ea 100644 --- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java +++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java @@ -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: *
{@code
  * public class Bean {
  *     private Foo foo;
@@ -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 findNonVoidWriteMethods(MethodDescriptor[] methodDescriptors) {
+	private List findCandidateWriteMethods(MethodDescriptor[] methodDescriptors) {
 		List matches = new ArrayList();
 		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];
diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java
index 7e46211922..183ffe4e2a 100644
--- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfoFactory.java
@@ -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;
 			}
 		}
diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
index 7da9710417..9ea8c569e7 100644
--- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java
@@ -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 {
 
diff --git a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java
index fd53b58af7..44062a9107 100644
--- a/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java
@@ -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) {
+		}
+	}
 }