Pular para o conteúdo principal

Capítulo 1: Inversão de Controle (IoC)

Introdução​

Bem-vindo! Neste capítulo, você vai entender o princípio fundamental que faz o Spring funcionar.

O que você vai aprender​

  1. O problema do alto acoplamento (e por que isso é ruim)
  2. A solução: Inversão de Controle
  3. Como o Spring implementa IoC
  4. O Container Spring e seu papel

Duração: 30 minutos


O Problema: Alto Acoplamento​

Situação Real​

Imagine que você está desenvolvendo uma aplicação de e-commerce. Você precisa enviar e-mails para os clientes.

❌ Abordagem Tradicional (Sem IoC)​

public class OrderService {

// Criamos diretamente o objeto EmailService
private EmailService emailService = new EmailService();

public void processOrder(Order order) {
// Lógica de negócio
saveOrder(order);

// Enviar e-mail de confirmação
emailService.sendEmail(order.getCustomerEmail(), "Pedido confirmado");
}
}

Por Que Isso é Problemático?​

Problema 1: Alto Acoplamento​

private EmailService emailService = new EmailService();
// ^^^^^^^^^^^^^^^^
// Dependência DIRETA!

Consequência:

  • OrderService DEPENDE DIRETAMENTE de EmailService
  • Impossível trocar a implementação facilmente
  • Difícil de testar

Problema 2: Impossível Trocar a Implementação​

Cenário: Você quer trocar do Gmail para o SendGrid para enviar os e-mails.

// Você precisa MODIFICAR OrderService
public class OrderService {
// Antes
private EmailService emailService = new EmailService();

// Depois - É PRECISO ALTERAR O CÓDIGO!
private SendGridEmailService emailService = new SendGridEmailService();
// ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
}

Problema: Alterar código que já funciona = risco de bugs!


Problema 3: Impossível de Testar​

Você quer testar OrderService sem enviar e-mails reais:

@Test
public void testProcessOrder() {
OrderService service = new OrderService();
Order order = new Order(...);

service.processOrder(order);

// PROBLEMA: Um e-mail real será enviado!
// Impossível "mockar" EmailService
}

Resumo do Problema​

OrderService ──────────> EmailService
(dependência direta)

❌ Alto acoplamento
❌ Difícil de modificar
❌ Difícil de testar
❌ Código rígido e frágil

A Solução: Inversão de Controle (IoC)​

Princípio Básico​

"Não crie suas dependências, peça por elas!"

Ao invés de:

private EmailService emailService = new EmailService();  // ❌ EU crio

Faça:

private EmailService emailService;  // ✅ ALGUÉM me fornece

O que é IoC?​

Inversão de Controle (IoC) = Inverter quem controla a criação dos objetos

Antes do IoC (Controle Normal)​

OrderService diz: "Preciso de um EmailService"
OrderService diz: "Vou criar eu mesmo com 'new'"

Com IoC (Controle Invertido)​

OrderService diz: "Preciso de um EmailService"
Spring diz: "Sem problemas, eu te dou!"
Spring cria EmailService e entrega para OrderService

O controle da criação é INVERTIDO: é o Spring que gerencia, não você!


✅ Abordagem com IoC (Injeção de Dependências)​

Código Melhorado​

@Service
public class OrderService {

// Não criamos o objeto
private final EmailService emailService;

// PEDIMOS o objeto via construtor
public OrderService(EmailService emailService) {
this.emailService = emailService;
}

public void processOrder(Order order) {
saveOrder(order);
emailService.sendEmail(order.getCustomerEmail(), "Pedido confirmado");
}
}

O Que Acontece​


Vantagens do IoC​

1. Baixo Acoplamento​

// OrderService NÃO conhece a implementação concreta
private final EmailService emailService;
// ^^^^^^^^^^^^
// Interface (abstração)

Você pode ter várias implementações:

public interface EmailService {
void sendEmail(String to, String message);
}

public class GmailEmailService implements EmailService {
// Envia via Gmail
}

public class SendGridEmailService implements EmailService {
// Envia via SendGrid
}

O Spring escolhe qual usar conforme a configuração!


2. Fácil de Modificar​

Troque a implementação SEM alterar OrderService:

// Configuração Spring
@Configuration
public class AppConfig {

@Bean
public EmailService emailService() {
// Antes
// return new GmailEmailService();

// Depois - Mudança em UM único lugar!
return new SendGridEmailService();
}
}

OrderService NÃO precisa ser alterado!


3. Fácil de Testar​

@Test
public void testProcessOrder() {
// Criamos um "fake" EmailService para testes
EmailService fakeEmailService = new EmailService() {
@Override
public void sendEmail(String to, String message) {
System.out.println("E-mail simulado enviado para: " + to);
// Nenhum e-mail real enviado!
}
};

// Injetamos o serviço fake
OrderService service = new OrderService(fakeEmailService);

Order order = new Order(...);
service.processOrder(order);

// ✅ Nenhum e-mail real enviado!
}

O Container Spring (ApplicationContext)​

O que é o Container?​

O Container Spring = O gerenciador de objetos do Spring

Suas responsabilidades:

  1. ✅ Criar os objetos (beans)
  2. ✅ Configurá-los
  3. ✅ Montá-los (injetar as dependências)
  4. ✅ Gerenciar seu ciclo de vida

Analogia​

Pense no Container Spring como um maestro:

Maestro (Container Spring)
↓
Músicos (Beans: Controller, Service, Repository)
↓
A orquestra toca música (A aplicação funciona)

O maestro:

  • ✅ Recruta os músicos (cria os beans)
  • ✅ Os posiciona corretamente (injeção)
  • ✅ Coordena o trabalho deles (gestão do ciclo de vida)

O Container em Ação​

// Ao iniciar a aplicação
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
// Esta linha inicia o Container Spring
SpringApplication.run(DemoApplication.class, args);
// ^^^
// O Container inicia aqui!
}
}

O que o Container faz ao iniciar:

1. Escanear as classes com @Component, @Service, @Repository, @Controller
↓
2. Criar as instâncias (beans)
↓
3. Resolver as dependências
↓
4. Injetar as dependências
↓
5. Aplicação pronta!

Visualização do Container​

O Container gerencia TODOS os objetos e suas dependências!


Exemplo Completo​

Sem IoC (Código Antigo)​

public class OrderService {
private EmailService emailService = new EmailService();
private PaymentService paymentService = new PaymentService();
private InventoryService inventoryService = new InventoryService();

public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}

Problemas:

  • ❌ 3 dependências criadas com new
  • ❌ Alto acoplamento em todo lugar
  • ❌ Impossível de testar
  • ❌ Rígido e frágil

Com IoC (Código Spring)​

@Service
public class OrderService {

// Declaração das dependências (sem 'new')
private final EmailService emailService;
private final PaymentService paymentService;
private final InventoryService inventoryService;

// Injeção via construtor
public OrderService(
EmailService emailService,
PaymentService paymentService,
InventoryService inventoryService
) {
this.emailService = emailService;
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}

public void processOrder(Order order) {
paymentService.charge(order);
inventoryService.reserve(order);
emailService.sendConfirmation(order);
}
}

Vantagens:

  • ✅ 0 uso de new (Spring gerencia tudo)
  • ✅ Baixo acoplamento
  • ✅ Fácil de testar (podemos injetar fakes)
  • ✅ Flexível e de fácil manutenção

Conceitos-Chave para Lembrar​

1. Inversão de Controle (IoC)​

"O Spring controla a criação dos objetos, não você."

Antes: Você cria seus objetos com new
Depois: O Spring cria seus objetos e os fornece para você


2. Injeção de Dependências (DI)​

"O Spring injeta (fornece) as dependências que você precisa."

Mecanismo: Via construtor, setters ou diretamente nos campos


3. Container Spring​

"O gerenciador central que cria e monta todos os objetos."

Responsabilidades:

  • Criar os beans
  • Resolver as dependências
  • Injetar as dependências
  • Gerenciar o ciclo de vida

Resumo do Capítulo​

O Problema​

// ❌ Alto acoplamento
private EmailService service = new EmailService();

Consequências:

  • Difícil de modificar
  • Difícil de testar
  • Código rígido

A Solução​

// ✅ IoC + Injeção de Dependências
@Service
public class OrderService {
private final EmailService service;

public OrderService(EmailService service) {
this.service = service;
}
}

Vantagens:

  • Baixo acoplamento
  • Fácil de modificar
  • Fácil de testar
  • Código flexível

Analogia Final​

IoC = Restaurante

Sem IoC:

Você: "Estou com fome"
Você: "Vou comprar os ingredientes"
Você: "Vou cozinhar"
Você: "Eu como"

Com IoC:

Você: "Estou com fome" (declaração da necessidade)
Restaurante: "Aqui está seu prato!" (Spring fornece)
Você: "Eu como" (você utiliza)

Você não cria o prato, ele é DADO para você. Isso é IoC!


Autoavaliação​

Responda a estas perguntas para verificar sua compreensão:

  1. O que é Inversão de Controle?
  2. Qual é o problema do alto acoplamento?
  3. Qual é o papel do Container Spring?
  4. Por que IoC facilita os testes?

Se você não conseguir responder, releia o capítulo!


Próximo Passo​

Agora que você entende POR QUE o IoC existe, vamos para o COMO: a Injeção de Dependências.

➡️ Capítulo 2: Injeção de Dependências

⬅️ Voltar ao Módulo 5