From badddeb0dcda388b661886134f5ceb56c89e7e6b Mon Sep 17 00:00:00 2001 From: Arnab Nandy Date: Thu, 30 Jul 2026 17:42:08 +0530 Subject: [PATCH] Ignore static get/is accessor methods in PropertyDescriptorUtils Prior to this commit, PropertyDescriptorUtils.determineBasicProperties() incorrectly recognized static `get` and `is` accessor methods as JavaBean read methods, in contrast to the standard java.beans.Introspector, which has always excluded static methods from property discovery. This regression was introduced in Spring Framework 6.0 when determineBasicProperties() replaced the delegation to java.beans.Introspector for the fast property-discovery path used by SimpleBeanInfoFactory. As a result, an unrelated static method such as a singleton accessor could be exposed as a bean property, and reflective access to such a property (for example, via BeanWrapperImpl) could lead to a StackOverflowError if the property's value recursively exposed the same static accessor. To address that, this commit adds Modifier.isStatic(...) checks to the `get` and `is` branches in determineBasicProperties(), mirroring the equivalent check already present in CachedIntrospectionResults.isPlainAccessor(). Static `set` methods continue to be supported as write methods, consistent with the existing behavior in ExtendedBeanInfo. See gh-37068 Closes gh-37081 Signed-off-by: Arnab Nandy --- .../beans/PropertyDescriptorUtils.java | 7 +++++-- ...DescriptorUtilsPropertyResolutionTests.java | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java index 7fb8ac07181..2bf6cb2dc1a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java @@ -19,6 +19,7 @@ package org.springframework.beans; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; @@ -71,11 +72,13 @@ abstract class PropertyDescriptorUtils { setter = true; nameIndex = 3; } - else if (methodName.startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != void.class) { + else if (methodName.startsWith("get") && method.getParameterCount() == 0 && + method.getReturnType() != void.class && !Modifier.isStatic(method.getModifiers())) { setter = false; nameIndex = 3; } - else if (methodName.startsWith("is") && method.getParameterCount() == 0 && method.getReturnType() == boolean.class) { + else if (methodName.startsWith("is") && method.getParameterCount() == 0 && + method.getReturnType() == boolean.class && !Modifier.isStatic(method.getModifiers())) { setter = false; nameIndex = 2; } diff --git a/spring-beans/src/test/java/org/springframework/beans/PropertyDescriptorUtilsPropertyResolutionTests.java b/spring-beans/src/test/java/org/springframework/beans/PropertyDescriptorUtilsPropertyResolutionTests.java index 11d772dedb1..f7a9f2f2cb8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/PropertyDescriptorUtilsPropertyResolutionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/PropertyDescriptorUtilsPropertyResolutionTests.java @@ -69,6 +69,13 @@ class PropertyDescriptorUtilsPropertyResolutionTests { assertReadAndWriteMethodsForClassAndId(pdMap, Number.class, null); } + @Test + void classWithStaticGetters() { + var pdMap = resolver.resolve(ClassWithStaticGetters.class); + + assertThat(pdMap).containsOnlyKeys("class"); + } + @Test void classWithOnlySetter() { var pdMap = resolver.resolve(ClassWithOnlySetter.class); @@ -134,6 +141,17 @@ class PropertyDescriptorUtilsPropertyResolutionTests { } } + static class ClassWithStaticGetters { + + public static Long getId() { + return 42L; + } + + public static boolean isActive() { + return true; + } + } + static class ClassWithOnlySetter { public void setId(Long id) {