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.

Why is this an issue?

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.

What is the potential impact?

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.

How to fix it in Spring

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.

Code examples

Noncompliant code example

@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
    }
}

Compliant solution

@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.

Noncompliant code example

@Configuration
public final class DatabaseConfig {  // Noncompliant

    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());  // Direct method call
    }
}

Compliant solution

@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);
    }
}

Resources

Documentation