Wstrzykiwanie przez konstruktor i zależności cykliczne
Proszę preferować wstrzykiwanie przez konstruktor ze względu na testowalność, wykrywać zależności cykliczne i usuwać je za pomocą @Lazy.
Wstrzykiwanie przez konstruktor i zależności cykliczne to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Trzy typy wstrzykiwania
Spring obsługuje wstrzykiwanie przez konstruktor (zalecane), wstrzykiwanie przez pole (@Autowired przy polu) oraz wstrzykiwanie przez setter. Wstrzykiwanie przez konstruktor jest preferowane ze względu na możliwość testowania i niezmienność.
Wstrzykiwanie przez konstruktor
Zależności należy zadeklarować jako parametry konstruktora. Spring wstrzyknie je automatycznie. Jeśli istnieje tylko jeden konstruktor, adnotacja @Autowired jest opcjonalna (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;
}
}Dlaczego preferowane jest wstrzykiwanie przez konstruktor
Wstrzykiwanie przez konstruktor zapewnia: (1) zależności są final — są niezmienne, (2) klasa jest zawsze w pełni zainicjalizowana, (3) testy jednostkowe nie wymagają zależności od Springa — wystarczy wywoł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 directlyWstrzykiwanie przez pole (należy go unikać)
Wstrzykiwanie przez pole za pomocą @Autowired jest zwięzłe, ale problematyczne: pól nie można oznaczyć jako final, bean nie jest w pełni zainicjalizowany po utworzeniu, a testy jednostkowe wymagają Springa lub narzędzi refleksji Mockito.
// Avoid in production code:
@Service
public class UserService {
@Autowired // hidden dependency
private UserRepository repo; // cannot be final
}Wstrzykiwanie przez setter w przypadku opcjonalnych zależności
Wstrzykiwanie przez setter należy stosować w przypadku opcjonalnych zależności, które mają sensowną wartość domyślną — setter należy oznaczyć adnotacją @Autowired(required = false).
@Service
public class NotificationService {
private SmsSender smsSender; // optional
@Autowired(required = false)
public void setSmsSender(SmsSender s) { this.smsSender = s; }
}Czym jest zależność cykliczna?
Zależność cykliczna występuje, gdy A wymaga B, a B wymaga A (bezpośrednio lub pośrednio). Spring wykrywa ją podczas uruchamiania i zgłasza wyjątek BeanCurrentlyInCreationException.
// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?Przerywanie cyklu: refaktoryzacja do trzeciej usługi
Najczystszym rozwiązaniem jest wydzielenie współdzielonej logiki do trzeciej usługi, od której ani A, ani B nie będą zależeć cyklicznie.
// 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) {} }Przerywanie cyklu: @Lazy
Jeden z punktów wstrzykiwania należy oznaczyć adnotacją @Lazy. Spring natychmiast tworzy proxy dla B, a rzeczywista instancja B jest tworzona dopiero wtedy, gdy A po raz pierwszy wywoła na niej metodę.
@Service
public class A {
private final B b;
public A(@Lazy B b) { this.b = b; } // breaks the cycle
}Przerywanie cyklu: wyszukiwanie za pomocą ApplicationContext
Do jednego beana należy wstrzyknąć ApplicationContext, a następnie leniwie wyszukać drugi bean za pomocą context.getBean() — zmniejsza to jednak możliwość testowania i czytelność kodu.
@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(); }
}Wstrzykiwanie przez setter w celu przerwania cyklu
Beany wstrzykiwane przez setter są najpierw tworzone (konstrukcja kończy się powodzeniem), a następnie ustawiane są ich zależności. Umożliwia to obsługę zależności cyklicznych kosztem zmienności.
@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+ domyślnie nie zezwala na zależności cykliczne. Należy włączać je jawnie tylko tymczasowo, na czas refaktoryzacji: spring.main.allow-circular-references=true.
# application.properties (temporary workaround only):
spring.main.allow-circular-references=trueSzybkie sprawdzenie
Dlaczego wstrzykiwanie przez konstruktor jest preferowane zamiast wstrzykiwania przez pole?
Podsumowanie
Należy preferować wstrzykiwanie przez konstruktor: jest niezmienne, możliwe do testowania i jawne. Wstrzykiwania przez pole należy unikać. Do przerywania zależności cyklicznych należy używać @Lazy lub wydzielić współdzieloną usługę. Spring Boot 2.6+ zgłasza zależności cykliczne podczas uruchamiania — należy traktować je jako ostrzeżenia dotyczące projektu.
Często zadawane pytania
Czy lekcja „Wstrzykiwanie przez konstruktor i zależności cykliczne” jest bezpłatna?
Tak — pełny tekst „Wstrzykiwanie przez konstruktor i zależności cykliczne” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Wstrzykiwanie przez konstruktor i zależności cykliczne”?
Proszę preferować wstrzykiwanie przez konstruktor ze względu na testowalność, wykrywać zależności cykliczne i usuwać je za pomocą @Lazy. Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Wstrzykiwanie przez konstruktor i zależności cykliczne”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Autokonfiguracja i startery Spring Boot
- Właściwości aplikacji i profile
- Wiązanie beanów: @Component, @Service, @Repository
- Wstrzykiwanie przez konstruktor i zależności cykliczne