No Spring, @Configuration não serve para qualquer classe

Existe um reflexo comum em projetos Spring:

a classe não está sendo encontrada, alguma dependência não injeta, um método com @Bean não parece funcionar ou o time quer "garantir" que o Spring olhe para aquele arquivo.

A solução aparece rápido:

"coloca @Configuration nessa classe."

Às vezes a aplicação até sobe.

E é justamente aí que mora o problema.

@Configuration não é uma anotação genérica para dar importância a uma classe.

Ela comunica uma intenção bem específica:

esta classe declara como parte do contexto Spring deve ser montada.

Quando essa intenção é ignorada, o código começa a misturar configuração, regra de negócio, criação de objetos e organização de pacotes no mesmo lugar.

O Spring continua funcionando.

Mas a leitura da aplicação piora.


@Configuration não é um @Component mais forte

Uma classe anotada com @Configuration também é registrada como Bean.

Isso leva muita gente a uma conclusão perigosa:

se @Configuration vira Bean, então ela seria uma espécie de @Component mais poderoso.

Não é esse o papel dela.

@Component diz:

"esta classe é um componente da aplicação e pode ser descoberta pelo container."

@Configuration diz outra coisa:

"esta classe contém declarações de configuração usadas para montar o contexto."

A diferença parece sutil, mas muda o desenho.

Compare:

@Service
public class FecharPedidoService {

    private final PedidoRepository pedidoRepository;

    public FecharPedidoService(PedidoRepository pedidoRepository) {
        this.pedidoRepository = pedidoRepository;
    }
}

Aqui existe um componente de aplicação.

Ele executa um caso de uso, depende de colaboradores e representa comportamento do domínio ou da camada de aplicação.

Agora veja outro tipo de classe:

@Configuration
public class PagamentoConfig {

    @Bean
    public GatewayPagamento gatewayPagamento(PagamentoProperties properties) {
        return new GatewayPagamentoHttp(properties.baseUrl(), properties.token());
    }
}

Aqui o ponto central não é executar regra de negócio.

É declarar como um objeto deve nascer dentro do contexto.

Essa classe não está modelando uma ação do sistema.

Ela está modelando uma decisão de montagem.


O erro aparece quando configuração vira gaveta

O uso ruim de @Configuration normalmente começa com uma intenção inocente.

O time cria uma classe para concentrar "coisas do Spring".

Depois essa classe ganha método @Bean.

Depois ganha leitura de property.

Depois ganha um helper.

Depois ganha uma regra condicional.

Depois alguém injeta um repository.

Quando percebe, a classe de configuração virou uma gaveta:

@Configuration
public class PedidoConfig {

    private final PedidoRepository pedidoRepository;

    public PedidoConfig(PedidoRepository pedidoRepository) {
        this.pedidoRepository = pedidoRepository;
    }

    @Bean
    public PoliticaFrete politicaFrete() {
        return new PoliticaFretePadrao();
    }

    public BigDecimal calcularTotal(Pedido pedido) {
        return pedidoRepository.buscarItens(pedido.id())
                .stream()
                .map(ItemPedido::valor)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

Esse código até pode compilar.

Pode até rodar.

Mas ele está contando uma história confusa.

PedidoConfig parece configuração, mas carrega regra de cálculo.

Tem método @Bean, mas também consulta dados.

Está no contexto do Spring, mas mistura montagem da aplicação com comportamento de negócio.

O problema não é "o Spring proíbe isso".

O problema é que a classe passou a ter um nome, uma anotação e uma responsabilidade que não combinam.


Classe de configuração deve explicar montagem

Uma boa classe @Configuration costuma responder perguntas de montagem:

  • qual implementação deve ser usada?
  • como uma biblioteca externa deve ser configurada?
  • qual adapter deve ser exposto para a aplicação?
  • qual objeto depende de properties para ser criado?
  • que infraestrutura precisa entrar no contexto?

Por exemplo:

@Configuration
public class NotificacaoConfig {

    @Bean
    public Notificador notificador(NotificacaoProperties properties) {
        if (properties.smsAtivo()) {
            return new NotificadorSms(properties.smsToken());
        }

        return new NotificadorEmail(properties.emailRemetente());
    }
}

Esse código ainda tem uma decisão.

Mas é uma decisão de montagem.

Ele não está enviando notificação.

Ele não está calculando pedido.

Ele não está acessando regra operacional do fluxo.

Ele está dizendo qual colaborador será entregue para o restante da aplicação.

Essa fronteira é importante porque conversa diretamente com o post sobre @Bean: @Bean existe para registrar explicitamente um objeto cuja criação precisa ser descrita.

@Configuration é o lugar natural para organizar essas declarações.

Não é o lugar natural para esconder comportamento que deveria estar em @Service, adapter, factory de domínio ou outro componente mais específico.


Nem toda classe com método auxiliar merece @Configuration

Outro sintoma comum é anotar uma classe com @Configuration porque ela tem métodos que ajudam a criar objetos.

Mas nem todo método de criação é configuração do Spring.

Veja este caso:

public class PedidoFactory {

    public Pedido criar(PedidoRequest request) {
        return new Pedido(request.clienteId(), request.itens());
    }
}

Essa classe pode ser só Java.

Dependendo do desenho, talvez nem precise ser Bean.

Se ela não precisa receber dependências do container, não precisa ser descoberta por scan e não representa infraestrutura compartilhada, colocar @Configuration nela só cria ruído.

Mesmo quando ela precisa ser injetada, o nome mais honesto talvez seja outro:

@Component
public class PedidoFactory {

    public Pedido criar(PedidoRequest request) {
        return new Pedido(request.clienteId(), request.itens());
    }
}

Agora a classe comunica seu papel real.

Ela é uma colaboradora da aplicação.

Não uma fonte de configuração do contexto.

Esse ponto se conecta com o post sobre nem tudo precisar virar Bean: antes de perguntar qual anotação usar, vale perguntar se aquele objeto deveria mesmo participar do container.


O nome da anotação deve bater com a responsabilidade

Em Spring, anotação também é linguagem de arquitetura.

Quando uma classe tem @Controller, esperamos entrada HTTP.

Quando tem @Repository, esperamos acesso a dados.

Quando tem @Service, esperamos comportamento de aplicação.

Quando tem @Configuration, esperamos montagem do contexto.

Se qualquer classe problemática recebe @Configuration, a anotação deixa de comunicar intenção.

Ela vira remendo.

E remendo em configuração costuma espalhar ambiguidade:

  • o leitor não sabe se a classe existe para declarar @Bean ou executar regra;
  • o time começa a procurar comportamento de negócio em arquivos de configuração;
  • decisões de montagem ficam misturadas com lógica operacional;
  • testes passam a subir contexto Spring para validar coisa que poderia ser Java simples;
  • refatorações ficam mais arriscadas porque a fronteira sumiu.

O Spring não consegue impedir toda escolha ruim de modelagem.

Ele oferece ferramentas.

A responsabilidade de usar cada ferramenta com intenção ainda é do projeto.


O ponto que vale fixar

@Configuration não serve para qualquer classe.

Ela deve marcar classes que declaram configuração do contexto Spring: métodos @Bean, imports, habilitações, integração com properties e decisões explícitas de montagem.

Se a classe executa regra de negócio, coordena caso de uso, adapta entrada, acessa dados ou apenas agrupa métodos auxiliares, provavelmente existe uma anotação melhor.

Ou nenhuma anotação.

No Spring, uma classe estar no container não é automaticamente sinal de bom desenho.

O ponto é mais simples e mais exigente:

o papel da classe precisa estar claro antes da anotação entrar.