This is an issue when a Spring @Configuration class is declared final. Spring needs to create CGLIB proxies for
configuration classes that contain @Bean methods, and the final modifier prevents the necessary subclassing.
Spring Framework uses CGLIB proxies to enforce singleton semantics for beans defined in @Configuration classes. When you call a
@Bean method from within another @Bean method, Spring intercepts the call through a proxy to return the singleton instance
rather than creating a new object.
The Java final keyword prevents class inheritance, which blocks CGLIB from creating the necessary subclass proxy. Without this proxy,
inter-bean method calls create new instances instead of retrieving the singleton, breaking Spring’s container management.
This typically manifests as a ConfigurationClassPostProcessor exception during application startup, preventing the Spring context from
initializing.
When Spring @Configuration classes are marked final, the application fails to start. The
ConfigurationClassPostProcessor throws an exception because CGLIB cannot create the required proxy subclass.
If this check were bypassed, the impact would be more subtle: inter-bean method calls would create duplicate instances instead of using singletons, leading to:
If you see this issue but your configuration class genuinely doesn’t call other @Bean methods directly, consider using
@Configuration(proxyBeanMethods = false) instead of suppressing the issue. This makes the intent explicit and improves startup
performance. Mark this as a false positive only if the class is not actually a @Configuration class or if the final modifier
is required by a framework constraint you cannot control.
Remove the final modifier from the @Configuration class. This allows Spring to create the CGLIB proxy needed to enforce
singleton semantics for inter-bean method calls.
@Configuration
public final class DatabaseConfig { // Noncompliant
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource()); // May create duplicate DataSource
}
}
@Configuration
public class DatabaseConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource()); // Uses singleton DataSource
}
}
If you don’t need inter-bean method calls and want to optimize startup performance, use @Configuration(proxyBeanMethods = false). This
disables CGLIB proxying, allowing the class to be final. However, you must use dependency injection instead of calling @Bean
methods directly.
@Configuration
public final class DatabaseConfig { // Noncompliant
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource()); // Direct method call
}
}
@Configuration(proxyBeanMethods = false)
public final class DatabaseConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) { // Injected parameter
return new JdbcTemplate(dataSource);
}
}