Iniezione tramite costruttore e dipendenze circolari
Preferisca l'iniezione tramite costruttore per migliorare la testabilità, rilevi le dipendenze circolari e le interrompa con @Lazy.
Iniezione tramite costruttore e dipendenze circolari è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.
Tre tipi di iniezione
Spring supporta l'iniezione tramite costruttore (consigliata), l'iniezione nei campi (@Autowired su un campo) e l'iniezione tramite setter. L'iniezione tramite costruttore è preferibile per la testabilità e l'immutabilità.
Iniezione tramite costruttore
Dichiari le dipendenze come parametri del costruttore. Spring le inietta automaticamente. Se esiste un solo costruttore, @Autowired è facoltativo (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;
}
}Perché è preferibile l'iniezione tramite costruttore
L'iniezione tramite costruttore garantisce che: (1) le dipendenze siano final, quindi immutabili; (2) la classe sia sempre completamente inizializzata; (3) i test unitari non dipendano da Spring: è sufficiente chiamare 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 directlyIniezione nei campi (da evitare)
L'iniezione nei campi con @Autowired è concisa, ma presenta dei problemi: i campi non possono essere final, il bean non è completamente inizializzato al termine della costruzione e i test unitari richiedono Spring o gli helper di reflection di Mockito.
// Avoid in production code:
@Service
public class UserService {
@Autowired // hidden dependency
private UserRepository repo; // cannot be final
}Iniezione tramite setter per le dipendenze facoltative
Utilizzi l'iniezione tramite setter per le dipendenze facoltative che hanno un valore predefinito ragionevole: contrassegni il setter con @Autowired(required = false).
@Service
public class NotificationService {
private SmsSender smsSender; // optional
@Autowired(required = false)
public void setSmsSender(SmsSender s) { this.smsSender = s; }
}Che cos'è una dipendenza circolare?
Si verifica una dipendenza circolare quando A richiede B e B richiede A, direttamente o transitivamente. Spring la rileva all'avvio e genera BeanCurrentlyInCreationException.
// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?Interrompere la circolarità: eseguire il refactoring verso un terzo servizio
La soluzione più pulita consiste nell'estrarre la logica condivisa in un terzo servizio da cui né A né B dipendano ciclicamente.
// 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) {} }Interrompere la circolarità: @Lazy
Annoti un punto di injection con @Lazy. Spring crea immediatamente un proxy per B; l'istanza reale di B viene creata solo quando A invoca per la prima volta un metodo su di essa.
@Service
public class A {
private final B b;
public A(@Lazy B b) { this.b = b; } // breaks the cycle
}Interrompere la circolarità: ricerca tramite ApplicationContext
Inietti ApplicationContext in un bean e cerchi l'altro bean in modo lazy tramite context.getBean(), ma questa soluzione riduce la testabilità e la chiarezza.
@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(); }
}Iniezione tramite setter per interrompere la circolarità
I bean con injection tramite setter vengono creati per primi (la costruzione va a buon fine), quindi vengono impostate le dipendenze. Questo consente le dipendenze circolari al costo della mutabilità.
@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+ disabilita le dipendenze circolari per impostazione predefinita. Le abiliti esplicitamente solo come misura temporanea durante il refactoring: spring.main.allow-circular-references=true.
# application.properties (temporary workaround only):
spring.main.allow-circular-references=trueVerifica rapida
Perché l'iniezione tramite costruttore è preferibile all'iniezione nei campi?
Riepilogo
Preferisca l'iniezione tramite costruttore: è immutabile, testabile ed esplicita. Eviti l'iniezione nei campi. Utilizzi @Lazy oppure estragga un servizio condiviso per interrompere le dipendenze circolari. Spring Boot 2.6+ segnala i riferimenti circolari all'avvio: li consideri avvisi di progettazione.
Domande Frequenti
La lezione «Iniezione tramite costruttore e dipendenze circolari» è gratuita?
Sì — il testo completo di «Iniezione tramite costruttore e dipendenze circolari» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.
Cosa imparerò in «Iniezione tramite costruttore e dipendenze circolari»?
Preferisca l'iniezione tramite costruttore per migliorare la testabilità, rilevi le dipendenze circolari e le interrompa con @Lazy. Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Java Academy?
Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Iniezione tramite costruttore e dipendenze circolari»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Java Academy?
Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Configurazione automatica e starter di Spring Boot
- Proprietà dell'applicazione e profili
- Collegamento dei bean: @Component, @Service, @Repository
- Iniezione tramite costruttore e dipendenze circolari