Final Configuration Class Blocks Bean Proxying

ID

java.config_classes_final

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

best-practice, reliability

Description

Reports @Configuration classes declared final when they also declare at least one @Bean method. Spring generates a CGLIB subclass of the configuration class so that a call from one @Bean method to another is intercepted and returns the already-created singleton instead of a fresh object. A final class cannot be subclassed, so that interception never happens.

Rationale

Without the subclass proxy, a call from one @Bean method to another runs as a plain Java method call: it builds a brand-new object instead of returning the bean already registered in the container. Depending on the Spring version, this either surfaces as a startup failure while the context is being built, or it silently produces duplicate instances of what should be a single shared bean.

// Bad: final blocks the CGLIB proxy that inter-bean calls rely on
@Configuration
public final class DataSourceConfig {

    @Bean
    public ConnectionPool connectionPool() {
        return new ConnectionPool();
    }

    @Bean
    public QueryRunner queryRunner() {
        return new QueryRunner(connectionPool()); // may get a second ConnectionPool
    }
}

Remediation

Drop the final modifier so Spring can create the proxy. If the class genuinely never calls one @Bean method from another, set @Configuration(proxyBeanMethods = false) instead: this tells Spring to skip the proxy, and final becomes safe again, but any cross-bean dependency must then be requested as a method parameter rather than through a direct call.

// Good: non-final, so Spring can proxy the inter-bean call
@Configuration
public class DataSourceConfig {

    @Bean
    public ConnectionPool connectionPool() {
        return new ConnectionPool();
    }

    @Bean
    public QueryRunner queryRunner() {
        return new QueryRunner(connectionPool()); // resolves to the managed singleton
    }
}

© 2026 Xygeni. All rights reserved