Nivel 2 · 20 min
Spring DI
El contenedor IoC de Spring es la base del ecosistema Spring. Entender el ciclo de vida de los beans, los estilos de inyección, los scopes y los proxies AOP es esencial para diagnosticar bugs misteriosos de Spring — dependencias nulas, errores de dependencias circulares y advice que silenciosamente no se dispara.
Contenedor IoC y ciclo de vida del bean
El contenedor IoC de Spring gestiona la creación, el cableado y la destrucción de beans. Ciclo de vida del bean: 1) El contenedor lee la configuración (anotaciones/XML), 2) Instancia del bean creada (constructor), 3) Dependencias inyectadas, 4) @PostConstruct llamado (init personalizado), 5) Bean listo para usar, 6) Al apagar: @PreDestroy llamado, 7) Bean destruido. ApplicationContext es el contenedor principal.
@Autowired vs inyección por constructor, Scopes de beans
La inyección por constructor es preferida sobre la inyección de campos con @Autowired por tres razones: 1) Los campos pueden declararse final (garantía de inmutabilidad), 2) La inyección por constructor hace las dependencias explícitas y visibles, 3) La inyección por constructor permite unit testing sin contenedor Spring. Scopes de beans: singleton (una instancia por contenedor, por defecto), prototype (nueva instancia por punto de inyección), request (uno por solicitud HTTP), session (uno por sesión HTTP).
Proxies AOP y dependencias circulares
Spring AOP funciona envolviendo beans en proxies CGLIB o JDK dinámicos. Por eso la auto-invocación no dispara el advice (@Transactional en un método que llama a otro método @Transactional en la misma clase es un error común — la segunda llamada va a través de this, no del proxy). Dependencias circulares: Spring puede resolver dependencias circulares entre beans singleton usando inyección de setters o campos. Las dependencias circulares por constructor fallan en el inicio.
Code example
// PREFERIDO: inyección por constructor
@Service
public class OrderService {
private final PaymentService payment;
private final InventoryService inventory;
public OrderService(PaymentService payment, InventoryService inventory) {
this.payment = payment;
this.inventory = inventory;
}
}
// Hooks del ciclo de vida
@Component
public class CacheService {
@PostConstruct
public void init() { /* calentar cache */ }
@PreDestroy
public void cleanup() { /* vaciar cache */ }
}
// Prototype en singleton via ObjectProvider
@Service
public class TaskRunner {
private final ObjectProvider<Worker> workerProvider;
public void run() {
Worker w = workerProvider.getObject();
w.execute();
}
}