Especialização em Backend com Java & Spring Boot • Demonstrações Práticas Compiláveis


💻 Código de Exemplo 01 — Desambiguação de Beans com @Qualifier e Injeção por Construtor

package com.empresa.api.notificacao;

public interface NotificadorService {
    void enviar(String destinatario, String mensagem);
}
package com.empresa.api.notificacao;

import org.springframework.stereotype.Service;

@Service("emailNotificador")
public class EmailNotificadorService implements NotificadorService {
    @Override
    public void enviar(String destinatario, String mensagem) {
        System.out.println("[EMAIL] Para " + destinatario + ": " + mensagem);
    }
}
package com.empresa.api.notificacao;

import org.springframework.stereotype.Service;

@Service("smsNotificador")
public class SmsNotificadorService implements NotificadorService {
    @Override
    public void enviar(String destinatario, String mensagem) {
        System.out.println("[SMS] Para " + destinatario + ": " + mensagem);
    }
}
package com.empresa.api.pedido;

import com.empresa.api.notificacao.NotificadorService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class PedidoService {

    private final NotificadorService notificador;

    public PedidoService(@Qualifier("smsNotificador") NotificadorService notificador) {
        this.notificador = notificador;
    }

    public void finalizarPedido(Long id, String contatoCliente) {
        notificador.enviar(contatoCliente, "Pedido " + id + " confirmado!");
    }
}

💻 Código de Exemplo 02 — Bean Scope Prototype vs Singleton

package com.empresa.api.pedido;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class ContextoDeCalculoPedido {
    private double totalAcumulado;

    public void somar(double valor) {
        this.totalAcumulado += valor;
    }

    public double getTotalAcumulado() {
        return totalAcumulado;
    }
}
package com.empresa.api.pedido;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class CalculadoraPedidoService {

    // ObjectProvider garante uma NOVA instância prototype a cada chamada,
    // mesmo estando injetado dentro de um Service singleton.
    private final ObjectProvider<ContextoDeCalculoPedido> contextoProvider;

    public CalculadoraPedidoService(ObjectProvider<ContextoDeCalculoPedido> contextoProvider) {
        this.contextoProvider = contextoProvider;
    }

    public double calcularTotal(double... itens) {
        ContextoDeCalculoPedido contexto = contextoProvider.getObject();
        for (double item : itens) {
            contexto.somar(item);
        }
        return contexto.getTotalAcumulado();
    }
}

💻 Código de Exemplo 03 — Ciclo de Vida com @PostConstruct e @PreDestroy

package com.empresa.api.cache;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class CacheDeCategoriasService {

    private final Map<Long, String> cache = new HashMap<>();

    @PostConstruct
    void carregarCache() {
        cache.put(1L, "Eletrônicos");
        cache.put(2L, "Livros");
        System.out.println("Cache de categorias carregado com " + cache.size() + " entradas.");
    }

    public String buscarNome(Long id) {
        return cache.getOrDefault(id, "Desconhecida");
    }

    @PreDestroy
    void limparCache() {
        cache.clear();
        System.out.println("Cache de categorias liberado no shutdown da aplicação.");
    }
}