0Pricing
Java Academy · Lekcja

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 directly

Wstrzykiwanie 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=true

Szybkie 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

  1. Autokonfiguracja i startery Spring Boot
  2. Właściwości aplikacji i profile
  3. Wiązanie beanów: @Component, @Service, @Repository
  4. Wstrzykiwanie przez konstruktor i zależności cykliczne
← Powrót do Java Academy