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
- O problema do alto acoplamento (e por que isso é ruim)
- A solução: Inversão de Controle
- Como o Spring implementa IoC
- 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:
OrderServiceDEPENDE DIRETAMENTE deEmailService- 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:
- ✅ Criar os objetos (beans)
- ✅ Configurá-los
- ✅ Montá-los (injetar as dependências)
- ✅ 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:
- O que é Inversão de Controle?
- Qual é o problema do alto acoplamento?
- Qual é o papel do Container Spring?
- 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.