Direct Bean Call Without Proxy
ID |
java.bean_methods_singleton_invoked_config |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
best-practice, reliability |
Description
Reports a method that calls a @Bean method directly (beanMethod() or this.beanMethod()) inside a @Configuration(proxyBeanMethods = false) class, when the called method has no scope annotation or is explicitly scoped singleton. Disabling proxyBeanMethods removes the CGLIB subclass that would otherwise intercept the call and return the already-created bean, so the call runs as an ordinary Java method invocation.
Rationale
proxyBeanMethods = false puts @Bean methods in "lite" mode: nothing intercepts calls to them, so every direct call builds a brand-new object. For a bean that is meant to be a shared singleton, each caller ends up with its own instance instead of the one already registered in the application context.
// Bad: connectionPool() is called directly, so every consumer gets its own pool
@Configuration(proxyBeanMethods = false)
class DataAccessConfig {
@Bean
public ConnectionPool connectionPool() {
return new ConnectionPool();
}
@Bean
public QueryRunner queryRunner() {
return new QueryRunner(connectionPool());
}
}
Remediation
Request the dependency as a method parameter instead of calling the @Bean method directly; Spring resolves the parameter from the context, so every caller shares the same instance regardless of proxying.
// Good: the pool is injected, so every caller shares the one managed instance
@Configuration(proxyBeanMethods = false)
class DataAccessConfig {
@Bean
public ConnectionPool connectionPool() {
return new ConnectionPool();
}
@Bean
public QueryRunner queryRunner(ConnectionPool connectionPool) {
return new QueryRunner(connectionPool);
}
}