Skip to content

BeanWrapperImpl discovers static getter methods (getInstance/getBean) as JavaBean properties [SF 7.0 regression] #37068

Description

@blackcoral88

Affects: Spring Framework 7.0.8 (regression since 6.0)

Summary

AbstractNestablePropertyAccessor / BeanWrapperImpl discovers public static getter methods as JavaBean properties. A @ConfigurationProperties class with public static Xxx getInstance() gets a read-only property named instance that returns the singleton object. This is a regression from Spring
Framework 5.x where CachedIntrospectionResults explicitly filtered static methods via Modifier.isStatic().

Root Cause

Spring Framework 6.0 replaced java.beans.Introspector-based property discovery with a custom "efficient method reflection pass." This reflection pass no longer checks Modifier.isStatic() on discovered getter methods, so public static getters like getInstance(), getBean() are registered as bean
properties alongside actual instance properties.

Reproduction

@ConfigurationProperties("app")
public class AppSettings {
    private String name;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    // Static getter — NOT a bean property, but BeanWrapperImpl treats it as one
    public static AppSettings getBean() {
        return SpringUtils.tryGetBean(AppSettings.class).orElseGet(AppSettings::new);
    }
}

BeanWrapperImpl wrapper = new BeanWrapperImpl(new AppSettings());
for (PropertyDescriptor pd : wrapper.getPropertyDescriptors()) {
    System.out.println(pd.getName());  // prints: "bean", "class", "name"
}
// Expected: only "name" and "class"
// Actual: "bean" also appears — static method incorrectly treated as instance property

Impact

This causes downstream failures. Most critically, spring-cloud-context 5.0.2's ConfigurationPropertiesRebinder.resetProperties() recurses into read-only non-simple properties. When the static getter returns the same singleton bean object, infinite recursion triggers StackOverflowError:

java.lang.StackOverflowError
    at org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler.getValue(BeanWrapperImpl.java:270)
    at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:623)
    at org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder.resetProperties(ConfigurationPropertiesRebinder.java:216)
    at org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder.resetProperties(ConfigurationPropertiesRebinder.java:231)
    at org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder.resetProperties(ConfigurationPropertiesRebinder.java:231)
    ... (repeats indefinitely)

Four beans in our project affected: ReloadableLdapProperties (getInstance()), SysConfigProperties (getInstance()), ApiSecurityProperties (getBean()), AppSettings (getBean()).

Spring Cloud mitigated the symptom in 5.0.3 (spring-cloud/spring-cloud-commons#1699) by adding an IdentityHashMap-based cycle guard to resetProperties(). However, the static getter is still incorrectly discovered as a propertythe cycle guard only prevents the crash, it doesn't address this root cause.

Expected Behavior

BeanWrapperImpl should NOT discover static methods as bean properties. Static methods do not operate on bean instances and are not part of the JavaBeans property model.

Suggested Fix

In the property discovery logic, filter out getter methods where Modifier.isStatic(method.getModifiers()) returns true.

Workarounds

1. Upgrade to spring-cloud-context 5.0.3+ (cycle guard prevents the crash downstream)
2. Add affected beans to spring.cloud.refresh.never-refreshable
3. Rename static getters to not follow getXxx() pattern (e.g., findInstance())
4. Add -Xss JVM flag to increase thread stack size (masks symptom, does not fix)

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions