|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Czy wiesz, że zależności w Springu powinniśmy wstrzykiwać przez konstruktor?" |
| 4 | +date: 2026-03-16T08:00:00+01:00 |
| 5 | +published: true |
| 6 | +didyouknow: false |
| 7 | +lang: pl |
| 8 | +author: bpietrowiak |
| 9 | +image: /assets/img/posts/2026-03-16-czy-wiesz-ze-zaleznosci-w-springu-powinnismy-wstrzykiwac-przez-konstruktor/thumbnail.webp |
| 10 | +description: "Poznaj zalety tego podejścia, przykłady kodu i wskazówki dotyczące testowania oraz bezpieczeństwa aplikacji." |
| 11 | +tags: |
| 12 | +- spring boot |
| 13 | +- java |
| 14 | +--- |
| 15 | + |
| 16 | +Czy wiesz, że sposób wstrzykiwania zależności w Springu może mieć ogromny wpływ na jakość Twojego kodu, jego bezpieczeństwo i łatwość testowania? |
| 17 | +Jeśli chcesz pisać lepsze aplikacje, warto poznać najważniejsze techniki i wybrać tę, która przynosi najlepsze efekty. |
| 18 | + |
| 19 | +## Czym jest wstrzykiwanie zależności? |
| 20 | + |
| 21 | +Wstrzykiwanie zależności (ang. *Dependency Injection*, DI) w Springu to kluczowy mechanizm, |
| 22 | +który umożliwia automatyczne zarządzanie zależnościami pomiędzy obiektami w aplikacji. |
| 23 | +Jest to część szerszego podejścia do programowania, znanego jako *Inversion of Control* (IoC), |
| 24 | +w którym zarządzanie tworzeniem obiektów i ich zależnościami przekazywane jest z aplikacji do kontenera IoC (w Springu jest nim Spring Container). |
| 25 | + |
| 26 | +## Metody wstrzykiwania zależności |
| 27 | + |
| 28 | +Możemy wyróżnić kilka sposobów wstrzykiwania zależności, z których każdy ma swoje zalety i ograniczenia. |
| 29 | +Poniżej przedstawiam najpopularniejsze techniki DI: |
| 30 | + |
| 31 | +- Wstrzykiwanie jawnie zdefiniowanym konstruktorem |
| 32 | +```java |
| 33 | +@Component |
| 34 | +public class OrderService { |
| 35 | + private final PaymentService paymentService; |
| 36 | + |
| 37 | + public OrderService(final PaymentService paymentService) { |
| 38 | + this.paymentService = paymentService; |
| 39 | + } |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +- Wstrzykiwanie przez konstruktor z wykorzystaniem adnotacji `@RequiredArgsConstructor` |
| 44 | +```java |
| 45 | +@Component |
| 46 | +@RequiredArgsConstructor |
| 47 | +public class OrderService { |
| 48 | + private final PaymentService paymentService; |
| 49 | +} |
| 50 | +``` |
| 51 | +*Adnotacja `@RequiredArgsConstructor` pochodzi z biblioteki Lombok i automatycznie generuje konstruktor przyjmujący wszystkie pola oznaczone jako `final` |
| 52 | +lub z adnotacją `@NonNull`.* |
| 53 | +- Wstrzykiwanie przez pola |
| 54 | +```java |
| 55 | +@Component |
| 56 | +public class OrderService { |
| 57 | + @Autowired |
| 58 | + private PaymentService paymentService; |
| 59 | +} |
| 60 | +``` |
| 61 | +*Wstrzykiwanie przez pola jest najmniej zalecanym podejściem, ponieważ utrudnia testowanie i nie pozwala na oznaczenie zależności jako finalne. |
| 62 | +Może być stosowane w wyjątkowych przypadkach, np. w bardzo prostych klasach lub kodzie legacy.* |
| 63 | + |
| 64 | + |
| 65 | +### @Qualifier - kiedy i jak używać? |
| 66 | + |
| 67 | +Adnotacja `@Qualifier` służy do wskazania konkretnego beana, gdy w kontekście Springa istnieje wiele beanów tego samego typu. |
| 68 | +Technicznie działa w każdym stylu wstrzykiwania, ale podejścia różnią się czytelnością i łatwością testowania. |
| 69 | + |
| 70 | +Najbardziej czytelnie: `@Qualifier` w konstruktorze |
| 71 | +```java |
| 72 | +@Component |
| 73 | +public class OrderService { |
| 74 | + private final PaymentService paymentService; |
| 75 | + |
| 76 | + public OrderService(@Qualifier("paymentService") PaymentService paymentService) { |
| 77 | + this.paymentService = paymentService; |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +Gdy zależność jest opcjonalna: `@Qualifier` w setterze |
| 83 | +```java |
| 84 | +@Component |
| 85 | +public class OrderService { |
| 86 | + private PaymentService paymentService; |
| 87 | + |
| 88 | + @Autowired |
| 89 | + public void setPaymentService( |
| 90 | + @Qualifier("paymentService") PaymentService paymentService) { |
| 91 | + this.paymentService = paymentService; |
| 92 | + } |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +Najmniej zalecane: `@Qualifier` na polu |
| 97 | +```java |
| 98 | +@Component |
| 99 | +public class OrderService { |
| 100 | + @Autowired |
| 101 | + @Qualifier("paymentService") |
| 102 | + private PaymentService paymentService; |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +Jeśli używasz Lombok (`@RequiredArgsConstructor`), możesz pozostać przy stylu konstruktorowym i jednocześnie oznaczyć pole adnotacją `@Qualifier`: |
| 107 | + |
| 108 | +```java |
| 109 | +@Component |
| 110 | +@RequiredArgsConstructor |
| 111 | +public class OrderService { |
| 112 | + @Qualifier("paymentService") |
| 113 | + private final PaymentService paymentService; |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +W takim wariancie dodaj w pliku `lombok.config`: |
| 118 | + |
| 119 | +``` |
| 120 | +lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier |
| 121 | +``` |
| 122 | + |
| 123 | +Dzięki temu Lombok przeniesie `@Qualifier` z pola do parametru wygenerowanego konstruktora. |
| 124 | + |
| 125 | +- Wstrzykiwanie przez settery (metody ustawiające) |
| 126 | +```java |
| 127 | +@Component |
| 128 | +public class OrderService { |
| 129 | + private PaymentService paymentService; |
| 130 | + |
| 131 | + @Autowired |
| 132 | + public void setPaymentService(PaymentService paymentService) { |
| 133 | + this.paymentService = paymentService; |
| 134 | + } |
| 135 | +} |
| 136 | +``` |
| 137 | +*Wstrzykiwanie przez settery może być uzasadnione, gdy zależność jest opcjonalna lub gdy pracujemy z kodem legacy, gdzie nie możemy zmienić konstruktora.* |
| 138 | + |
| 139 | + |
| 140 | +## Którą metodę powinniśmy wykorzystywać i dlaczego? |
| 141 | + |
| 142 | +Rekomendowanym podejściem jest wykorzystywanie wstrzykiwania przez konstruktor. Oto powody, dla których to podejście jest preferowane: |
| 143 | + |
| 144 | +- **Wymuszenie przekazania zależności podczas tworzenia obiektu** – wstrzykiwanie przez konstruktor gwarantuje, |
| 145 | +że wszystkie wymagane zależności zostaną dostarczone w momencie tworzenia instancji obiektu. Dzięki temu unikamy sytuacji, |
| 146 | +w której klasa może być używana bez pełnych zależności, co mogłoby prowadzić do błędów w runtime. |
| 147 | +- **Niezmienność obiektu** – przypisanie zależności poprzez konstruktor oznacza, że pola te mogą być oznaczone jako `final`, |
| 148 | +co zapewnia ich niezmienność i chroni przed niepożądanymi modyfikacjami w trakcie cyklu życia obiektu. Taka konstrukcja promuje czystszy i bardziej bezpieczny kod. |
| 149 | +- **Testy jednostkowe** – wstrzykiwanie przez konstruktor ułatwia testowanie, |
| 150 | +ponieważ możemy ręcznie dostarczać zależności (np. mocki) bez potrzeby używania takich narzędzi wspomagających, jak refleksja. |
| 151 | +To ułatwia pisanie testów jednostkowych i pozwala na zachowanie pełnej kontroli nad zależnościami podczas testowania. |
| 152 | + |
| 153 | + |
| 154 | +Wstrzykiwanie przez konstruktor wspiera zasady **SOLID**, w szczególności: |
| 155 | + |
| 156 | +- **Single Responsibility Principle (SRP)** – dzięki tej metodzie klasa ma jasno zdefiniowane odpowiedzialności, |
| 157 | +a zarządzanie zależnościami odbywa się na poziomie konstrukcji obiektu. |
| 158 | +- **Dependency Inversion Principle (DIP)** – poprzez konstruktor, zależności są wprowadzane od zewnątrz, |
| 159 | +co wzmacnia niezależność od szczegółowych implementacji. |
| 160 | + |
| 161 | +Wstrzykiwanie przez konstruktor sprawia, że zależności klasy są jasno widoczne i wyraźnie zadeklarowane w jej definicji. |
| 162 | +Programista, przeglądając kod, natychmiast widzi, jakie komponenty są wymagane do działania klasy. |
| 163 | + |
| 164 | +W przypadku wstrzykiwania przez konstruktor łatwiej jest zidentyfikować brakujące zależności lub problemy z ich konfiguracją podczas uruchamiania aplikacji, |
| 165 | +ponieważ Spring od razu poinformuje nas o braku zależności, której nie można dostarczyć. |
| 166 | + |
| 167 | +Konstruktor pomaga w szybszym wykrywaniu problemów z cyklicznymi zależnościami, które mogą występować w innych formach wstrzykiwania (np. wstrzykiwanie przez pola). |
| 168 | +Spring będzie w stanie zidentyfikować takie sytuacje już na etapie konfigurowania obiektów, co ułatwia ich eliminację. |
| 169 | + |
| 170 | +## Podsumowanie |
| 171 | + |
| 172 | +Wstrzykiwanie zależności przez konstruktor to najlepsza praktyka w aplikacjach Spring. |
| 173 | +Zapewnia bezpieczeństwo, czytelność kodu, łatwość testowania i zgodność z zasadami SOLID. |
| 174 | +Warto przyjąć to podejście jako domyślny standard w każdym projekcie, a inne metody rezerwować dla szczególnych przypadków (opcjonalne zależności, legacy code). |
| 175 | +Dzięki temu Twój kod będzie bardziej niezawodny i łatwiejszy w utrzymaniu. |
0 commit comments