Injeção de Dependências (Dependency Injection) em Java: guia completo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: O que é Injeção de Dependência e Por Que Você Precisa Dela em Java

No desenvolvimento Java moderno, a limpeza do código e a testabilidade estão entre os principais requisitos para um profissional. A Injeção de Dependência (DI) é um padrão de design que permite tornar seu código fracamente acoplado, flexível e facilmente mantível. Em vez de um objeto criar suas próprias dependências (por exemplo, via new), a DI as "injeta" de fora. Esta é uma parte fundamental do princípio Inversão de Controle (IoC).

Neste artigo, vamos detalhar como a Injeção de Dependência funciona em Java, analisar três maneiras principais de implementá-la (via construtor, setters e interface) e mostrar como a DI é usada em projetos reais, incluindo o popular framework Spring.

Por Que Seu Código se Torna Frágil Sem DI?

Imagine uma classe Car que cria um motor dentro de si mesma:

public class Car {
    private Engine engine;

    public Car() {
        this.engine = new PetrolEngine(); // Acoplamento rígido
    }

    public void start() {
        engine.run();
    }
}

Os problemas com essa abordagem são óbvios:

  • Impossível de reutilizar: Você não conseguirá substituir facilmente PetrolEngine por ElectricEngine.
  • Difícil de testar: Para testes unitários, você terá que criar um motor real em vez de um mock.
  • Violação do SRP: A classe Car não é responsável apenas por gerenciar o carro, mas também por criar o motor.

A Injeção de Dependência resolve esses problemas delegando a criação de dependências para código externo (um container ou outra classe).

Três Maneiras de Injetar Dependências em Java

Em Java, existem três abordagens principais para implementar DI. Vamos analisar cada uma usando um sistema de notificação como exemplo.

1. Injeção por Construtor

Este é o método mais preferido. As dependências são passadas através de parâmetros do construtor. Isso garante que o objeto sempre será criado em um estado válido e que as dependências não podem ser alteradas após a criação (imutabilidade).

public interface MessageService {
    void sendMessage(String message, String recipient);
}

public class EmailService implements MessageService {
    @Override
    public void sendMessage(String message, String recipient) {
        System.out.println("Email enviado para " + recipient + ": " + message);
    }
}

public class NotificationSender {
    private final MessageService messageService;

    // Injeção de dependência via construtor
    public NotificationSender(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String userEmail, String msg) {
        messageService.sendMessage(msg, userEmail);
    }
}

// Uso
public class Main {
    public static void main(String[] args) {
        MessageService emailService = new EmailService();
        NotificationSender sender = new NotificationSender(emailService); // DI
        sender.notifyUser("user@example.com", "Olá!");
    }
}

2. Injeção por Setter

Esta é adequada para dependências opcionais ou quando uma dependência pode ser alterada durante o tempo de vida do objeto. No entanto, o objeto pode ser criado em um estado incompleto se você esquecer de chamar o setter.

public class NotificationSender {
    private MessageService messageService;

    // Setter para injeção de dependência
    public void setMessageService(MessageService messageService) {
        this.messageService = messageService;
    }

    public void notifyUser(String userEmail, String msg) {
        if (messageService != null) {
            messageService.sendMessage(msg, userEmail);
        } else {
            System.o                

Blogs

Book Recommendations