Por que Abandonar @Autowired em Campos e Adotar Injeção via Construtor
Diego de Sousa Brandão

Diego de Sousa Brandão @diegobrandao

About: Optional.empty();

Location:
Brasil
Joined:
Jun 14, 2022

Por que Abandonar @Autowired em Campos e Adotar Injeção via Construtor

Publish Date: Jul 15
6 0

A injeção de dependência via construtor representa uma evolução natural das práticas de desenvolvimento com Spring Framework. Este documento apresenta os fundamentos técnicos que justificam essa transição e como implementá-la efetivamente em projetos modernos.

Contextualização

O Spring Framework oferece três formas principais de injeção de dependência: por campo (field injection), por setter e por construtor. Embora a injeção por campo seja visualmente mais limpa e requeira menos código, ela apresenta limitações significativas que impactam a qualidade, manutenibilidade e robustez das aplicações.

Principais Benefícios da Injeção via Construtor

1. Transparência e Dependências Explícitas

A injeção via construtor expõe claramente todas as dependências de uma classe em sua assinatura. Esta transparência facilita a compreensão do código, permitindo que desenvolvedores identifiquem rapidamente quais componentes uma classe necessita para funcionar corretamente.

Exemplo prático:

// Injeção por campo - dependências ocultas
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private EmailService emailService;
    // Dependências não são evidentes na assinatura da classe
}

// Injeção por construtor - dependências explícitas
@Service
public class UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;

    public UserService(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Testabilidade Aprimorada

A injeção via construtor elimina a dependência do contexto Spring durante os testes unitários. As classes podem ser instanciadas diretamente, com mocks injetados como parâmetros do construtor, resultando em testes mais rápidos e independentes.

Vantagens nos testes:

  • Eliminação da necessidade de reflection
  • Testes mais performáticos
  • Maior simplicidade na configuração de mocks
  • Independência do framework Spring nos testes unitários

3. Princípio Fail-Fast

A injeção por campo permite a criação de objetos mesmo quando dependências obrigatórias não estão disponíveis, resultando em NullPointerException apenas durante a execução. A injeção via construtor impede a criação do objeto sem suas dependências, tornando problemas visíveis imediatamente durante a inicialização da aplicação.

4. Imutabilidade e Thread Safety

Apenas através da injeção via construtor é possível definir campos de dependência como final. Esta imutabilidade oferece:

  • Segurança de thread: Campos final são thread-safe por natureza
  • Prevenção de efeitos colaterais: Impossibilita modificações acidentais das dependências
  • Clareza conceitual: Facilita o raciocínio sobre o estado dos objetos

5. Detecção Precoce de Dependências Circulares

Ciclos de dependência são detectados imediatamente durante a inicialização dos beans quando se utiliza injeção via construtor. Com injeção por campo, esses problemas podem permanecer ocultos até a execução específica do código afetado.

6. Incentivo ao Bom Design

A injeção via construtor evidencia classes com muitas dependências, naturalmente incentivando a aplicação do Princípio da Responsabilidade Única (SRP). Construtores com muitos parâmetros sinalizam a necessidade de refatoração e decomposição de responsabilidades.

7. Alinhamento com Práticas Modernas do Spring

Desde o Spring 4.3, o framework suporta injeção automática via construtor para classes com construtor único, dispensando a anotação @Autowired. Esta é a abordagem oficialmente recomendada pela equipe do Spring.

Implementação Prática

Padrão Recomendado (Sem Lombok)

@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
    private final NotificationService notificationService;

    public OrderService(OrderRepository orderRepository, 
                       PaymentService paymentService,
                       NotificationService notificationService) {
        this.orderRepository = orderRepository;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }

    // métodos da classe...
}
Enter fullscreen mode Exit fullscreen mode

Com Lombok (Quando Apropriado)

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
    private final NotificationService notificationService;

    // métodos da classe...
}
Enter fullscreen mode Exit fullscreen mode

Estratégia de Migração

Para Código Legado

  1. Priorização: Inicie por classes críticas e com maior cobertura de testes
  2. Refatoração gradual: Migre uma classe por vez para minimizar riscos
  3. Testes de regressão: Garanta que a funcionalidade permanece inalterada
  4. Validação: Verifique se não há dependências circulares expostas

Para Novos Desenvolvimentos

  • Adote injeção via construtor como padrão desde o início
  • Configure linters e ferramentas de análise estática para detectar uso de @Autowired em campos
  • Estabeleça como prática obrigatória em code reviews

Considerações Especiais

Dependências Opcionais

Para dependências opcionais, utilize @Autowired(required = false) no construtor ou considere padrões como Optional:

public UserService(UserRepository userRepository, 
                   @Autowired(required = false) CacheService cacheService) {
    this.userRepository = userRepository;
    this.cacheService = Optional.ofNullable(cacheService);
}
Enter fullscreen mode Exit fullscreen mode

Performance

A injeção via construtor não apresenta overhead significativo em relação à injeção por campo. Os benefícios em qualidade e manutenibilidade superam amplamente qualquer diferença marginal de performance.

Conclusão

A adoção da injeção via construtor representa um investimento na qualidade de longo prazo do código. Os benefícios em testabilidade, clareza, robustez e alinhamento com as melhores práticas do Spring justificam plenamente essa transição.

Esta mudança não é apenas uma questão de preferência pessoal, mas uma evolução natural em direção a um código mais robusto, testável e maintível, alinhado com os princípios modernos de desenvolvimento de software.


Referências Bibliográficas

  1. Why Field Injection is Not Recommended - Baeldung

  2. Why Spring Developers Prefer Constructor Injection - Umesh Kumar Yadav

  3. A Deep Dive Into Why @Autowired Can Harm Your Spring Application - MEsfandiari

  4. Stop Using @Autowired in Spring - Himanshu

  5. Why You Should Stop Using @Autowired in Spring - Pratham Karia

  6. Spring Doesn't Recommend @Autowired Anymore - Dulan Jaya Sandaruwan

Comments 0 total

    Add comment