0Pricing
Java Academy · Lección

Inyección por constructor y dependencias circulares

Prefiera la inyección por constructor para facilitar las pruebas, detecte dependencias circulares y rómpalas con @Lazy.

Inyección por constructor y dependencias circulares es una lección gratuita de Java Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Java Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Java Academy incluye 4 lecciones en total.

Tres tipos de inyección

Spring admite la inyección por constructor (recomendada), la inyección por campo (@Autowired en un campo) y la inyección por setter. Se prefiere la inyección por constructor por su facilidad de prueba y su inmutabilidad.

Inyección por constructor

Declare las dependencias como parámetros del constructor. Spring las inyecta automáticamente. Si solo hay un constructor, @Autowired es opcional (Spring Boot 2.1+).

@Service
public class OrderService {
    private final OrderRepository repo;
    private final EmailService email;
    // @Autowired is optional when there is only one constructor
    public OrderService(OrderRepository repo, EmailService email) {
        this.repo  = repo;
        this.email = email;
    }
}

Por qué se prefiere la inyección por constructor

Inyección por constructor: (1) las dependencias son final, por lo que son inmutables; (2) la clase siempre se inicializa por completo; (3) no requiere una dependencia de Spring en las pruebas unitarias: basta con llamar a new OrderService(mockRepo, mockEmail).

// Unit test — no Spring context needed:
OrderRepository mockRepo  = mock(OrderRepository.class);
EmailService    mockEmail = mock(EmailService.class);
OrderService    service   = new OrderService(mockRepo, mockEmail);
// Test service logic directly

Inyección por campo (evítela)

La inyección por campo con @Autowired es concisa, pero presenta problemas: los campos no pueden ser final, el bean no se inicializa por completo después de su construcción y las pruebas unitarias requieren Spring o las utilidades de reflexión de Mockito.

// Avoid in production code:
@Service
public class UserService {
    @Autowired  // hidden dependency
    private UserRepository repo; // cannot be final
}

Inyección por setter para dependencias opcionales

Use la inyección por setter para dependencias opcionales que tengan un valor predeterminado adecuado; marque el setter con @Autowired(required = false).

@Service
public class NotificationService {
    private SmsSender smsSender; // optional
    @Autowired(required = false)
    public void setSmsSender(SmsSender s) { this.smsSender = s; }
}

¿Qué es una dependencia circular?

Una dependencia circular ocurre cuando A requiere B y B requiere A (directa o transitivamente). Spring la detecta al iniciar la aplicación y lanza BeanCurrentlyInCreationException.

// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?

Romper la circularidad: refactorizar a un tercer servicio

La solución más limpia es extraer la lógica compartida a un tercer servicio del que ni A ni B dependan de forma cíclica.

// Before: A <-> B
// After: A -> C, B -> C (no cycle)
@Service class SharedLogic { ... }
@Service class A { A(SharedLogic s) {} }
@Service class B { B(SharedLogic s) {} }

Romper la circularidad: @Lazy

Anote un punto de inyección con @Lazy. Spring crea inmediatamente un proxy para B; la instancia real de B solo se crea cuando A llama por primera vez a uno de sus métodos.

@Service
public class A {
    private final B b;
    public A(@Lazy B b) { this.b = b; } // breaks the cycle
}

Romper la circularidad: búsqueda en ApplicationContext

Inyecte ApplicationContext en uno de los beans y busque el otro de forma diferida mediante context.getBean(), pero esto reduce la facilidad de prueba y la claridad.

@Service
public class A implements ApplicationContextAware {
    private ApplicationContext ctx;
    public void setApplicationContext(ApplicationContext c) { this.ctx = c; }
    public void doWork() { ctx.getBean(B.class).help(); }
}

Inyección por setter para romper la circularidad

Los beans con inyección por setter se crean primero (la construcción se completa) y después se establecen sus dependencias. Esto permite las dependencias circulares, a costa de la mutabilidad.

@Service
public class A {
    private B b;
    @Autowired public void setB(B b) { this.b = b; } // Spring sets after construction
}

spring.main.allow-circular-references (Spring Boot 2.6+)

Spring Boot 2.6+ prohíbe las dependencias circulares de forma predeterminada. Actívelas explícitamente solo como medida temporal mientras refactoriza: spring.main.allow-circular-references=true.

# application.properties (temporary workaround only):
spring.main.allow-circular-references=true

Comprobación rápida

¿Por qué se prefiere la inyección por constructor a la inyección por campo?

Resumen

Prefiera la inyección por constructor: es inmutable, fácil de probar y explícita. Evite la inyección por campo. Use @Lazy o extraiga un servicio compartido para romper las dependencias circulares. Spring Boot 2.6+ señala las referencias circulares al iniciar la aplicación; considérelas advertencias de diseño.

Preguntas frecuentes

¿La lección «Inyección por constructor y dependencias circulares» es gratis?

Sí — el texto completo de «Inyección por constructor y dependencias circulares» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Java Academy, actualiza a CoddyKit PRO. El curso de Java Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Inyección por constructor y dependencias circulares»?

Prefiera la inyección por constructor para facilitar las pruebas, detecte dependencias circulares y rómpalas con @Lazy. Practicas Java Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Java Academy?

No se requiere experiencia previa. Java Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Inyección por constructor y dependencias circulares»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Java Academy?

Sí. Cada lección de Java Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Autoconfiguración y starters de Spring Boot
  2. Propiedades de la aplicación y perfiles
  3. Inyección de beans: @Component, @Service, @Repository
  4. Inyección por constructor y dependencias circulares
← Volver a Java Academy