Czym dokładnie jest Field Injection i jak tego uniknąć?

131

Czytałem w niektórych postach o Spring MVC i Portletach, że iniekcja pola nie jest zalecana. Jak rozumiem, wstrzyknięcie pola ma miejsce, gdy wstrzykujesz Fasolę w @Autowirednastępujący sposób:

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

W trakcie moich badań czytałem również o iniekcji konstruktora :

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

Jakie są zalety i wady obu tych typów zastrzyków?


EDYCJA 1: Ponieważ to pytanie jest oznaczone jako duplikat tego pytania, sprawdziłem je. Ponieważ nie ma żadnych przykładów kodu ani w pytaniu, ani w odpowiedziach, nie jest dla mnie jasne, czy zgaduję, jakiego typu wtrysku używam.

T. Jung
źródło
3
Jeśli iniekcja polowa jest tak zła, jak opisujesz, dlaczego Wiosna na to pozwala? Wstrzykiwanie pola ma swoje zalety, ponieważ czyni kod bardziej czytelnym i mniej szczegółowym. Jeśli jesteś wystarczająco zdyscyplinowany w swoim kodowaniu, możesz być pewien, że nic się nie zepsuje, nawet jeśli użyjesz iniekcji terenowej.
popioły
1
@ashes Ponieważ była to wtedy fajna funkcja, a jej konsekwencje nie zostały do ​​końca przemyślane. Z tego samego powodu, który Date(int,int,int)istnieje.
chrylis

Odpowiedzi:

227

Rodzaje wtrysku

Istnieją trzy opcje wstrzykiwania zależności do fasoli:

  1. Przez konstruktora
  2. Poprzez setery lub inne metody
  3. Poprzez refleksję bezpośrednio w pola

Używasz opcji 3. To właśnie się dzieje, gdy używasz @Autowiredbezpośrednio na swoim polu.


Wytyczne dotyczące wtrysku

Ogólne wytyczne, które są zalecane przez Spring (zobacz sekcje dotyczące DI opartego na konstruktorze lub DI opartego na seterze ) są następujące:

  • W przypadku obowiązkowych zależności lub w przypadku dążenia do niezmienności należy użyć iniekcji konstruktora
  • W przypadku zależności opcjonalnych lub zmiennych użyj iniekcji ustawiającej
  • W większości przypadków należy unikać iniekcji w terenie

Wady iniekcji w terenie

Powody, dla których iniekcja w terenie jest niezadowolona, ​​są następujące:

  • Nie można tworzyć niezmiennych obiektów, tak jak w przypadku iniekcji konstruktora
  • Twoje zajęcia są ściśle powiązane z kontenerem DI i nie mogą być używane poza nim
  • Nie można utworzyć wystąpienia klas (na przykład w testach jednostkowych) bez odbicia. Potrzebujesz kontenera DI, aby je utworzyć, co sprawia, że ​​testy bardziej przypominają testy integracji
  • Twoje rzeczywiste zależności są ukryte z zewnątrz i nie są odzwierciedlone w interfejsie (ani konstruktorach, ani metodach)
  • Naprawdę łatwo jest mieć około dziesięciu zależności. Gdybyś używał iniekcji konstruktora, miałbyś konstruktora z dziesięcioma argumentami, które sygnalizowałyby, że coś jest podejrzane. Ale możesz dodawać wstrzykiwane pola za pomocą wstrzykiwania pola przez czas nieokreślony. Posiadanie zbyt wielu zależności jest czerwoną flagą, że klasa zwykle robi więcej niż jedną rzecz i że może to naruszać zasadę pojedynczej odpowiedzialności.

Wniosek

W zależności od potrzeb należy przede wszystkim używać iniekcji konstruktora lub mieszanki iniekcji konstruktora i ustawiacza. Wstrzyknięcie w terenie ma wiele wad i należy go unikać. Jedyną zaletą wtrysku polowego jest to, że wygodniej jest pisać, co nie przeważa nad wszystkimi wadami.


Dalsza lektura

Napisałem artykuł na blogu o tym, dlaczego wstrzyknięcie pola zwykle nie jest zalecane: Wstrzyknięcie pola z zależnością uważane za szkodliwe .

Vojtech Ruzicka
źródło
12
Generalnie nie jest dobrym pomysłem i nie jest miło mówić światu, że „należy unikać iniekcji w terenie”. Pokaż za i przeciw i pozwól innym decydować o sobie;) Wiele osób ma inne doświadczenia i własny sposób patrzenia na sprawy.
dieter
6
Może tak być w tym przypadku, ale są też inne przypadki, w których społeczność doszła do ogólnego konsensusu, aby coś zniechęcić. Weźmy na przykład notację węgierską.
Jannik
Podajesz kilka dobrych punktów, takich jak testowalność i widoczność zależności, ale nie zgadzam się ze wszystkimi. Wtrysk konstruktora nie ma wad? Pożądane może być posiadanie 5 lub 6 pól do wstrzyknięcia w klasie, która wykonuje rzeczywiste kompozycje rozmów. Nie zgadzam się również z Tobą co do niezmienności. Posiadanie pól końcowych nie jest wymagane, aby mieć niezmienną klasę. To jest lepsze. Co jest zupełnie inne.
davidxxx
Myślę, że miałeś na myśli „Dla obowiązkowych zależności lub dążenia do niezmienności
Alex Terreaux
1
Miałem na myśli link na początku odpowiedzi, który prowadzi do dokumentów wiosennych
Vojtech Ruzicka
47

Jest to jedna z niekończących się dyskusji na temat tworzenia oprogramowania, ale główni wpływowi w branży są coraz bardziej opiniowani na ten temat i zaczęli sugerować wstrzyknięcie konstruktora jako lepszą opcję.

Wstrzyknięcie konstruktora

Plusy:

  • Lepsza testowalność . W testach jednostkowych nie potrzebujesz żadnej biblioteki symulującej ani kontekstu Spring. Możesz utworzyć obiekt, który chcesz przetestować za pomocą nowego słowa kluczowego. Takie testy są zawsze szybsze, ponieważ nie polegają na mechanizmie odbicia. ( To pytanie zostało zadane 30 minut później. Gdyby autor użył iniekcji konstruktora, to by się nie pojawiło).
  • Niezmienność . Po ustawieniu zależności nie można ich zmienić.
  • Bezpieczniejszy kod . Po wykonaniu konstruktora Twój obiekt jest gotowy do użycia, ponieważ możesz zweryfikować wszystko, co zostało przekazane jako parametr. Obiekt może być gotowy lub nie, nie ma stanu pomiędzy. W przypadku iniekcji w terenie wprowadzasz etap pośredni, gdy obiekt jest kruchy.
  • Czystsze wyrażenie obowiązkowych zależności . Wstrzyknięcie polowe jest w tej kwestii niejednoznaczne.
  • Sprawia, że ​​programiści myślą o projekcie . dit napisał o konstruktorze z 8 parametrami, co w rzeczywistości jest oznaką złego projektu i anty-wzorcem obiektu Boga . Nie ma znaczenia, czy klasa ma 8 zależności w swoim konstruktorze czy w polach, zawsze jest źle. Ludzie są bardziej niechętni do dodawania większej ilości zależności do konstruktora niż poprzez pola. Działa jako sygnał dla twojego mózgu, że powinieneś zatrzymać się na chwilę i pomyśleć o strukturze kodu.

Cons:

  • Więcej kodu (ale nowoczesne IDE łagodzą ból).

Zasadniczo iniekcja w terenie jest odwrotna.

Daniel Olszewski
źródło
1
testowalność, tak, to był dla mnie koszmar kpić z fasoli, którą wstrzyknięto na pole. Kiedyś użyłem zastrzyku contsructor, nie muszę robić żadnych niepotrzebnych kpin
kenobiwan
25

Kwestia gustu. To Twoja decyzja.

Ale mogę wyjaśnić, dlaczego nigdy nie używam wtrysku konstruktora .

  1. Nie chcę, aby zaimplementować konstruktor dla wszystkich moich @Service, @Repositoryi @Controllerfasoli. To znaczy, jest około 40-50 ziaren lub więcej. Za każdym razem, gdybym dodawał nowe pole, musiałbym rozbudowywać konstruktora. Nie. Nie chcę tego i nie muszę.

  2. A jeśli twoja fasola (usługa lub kontroler) wymaga wstrzyknięcia dużej ilości innych ziaren? Konstruktor z parametrami 4+ jest bardzo brzydki.

  3. Jeśli używam CDI, konstruktor mnie nie dotyczy.


EDYCJA nr 1 : Vojtech Ruzicka powiedział:

class ma zbyt wiele zależności i prawdopodobnie narusza zasadę pojedynczej odpowiedzialności i powinien zostać zmieniony

Tak. Teoria i rzeczywistość. Oto przykład: DashboardControllermapowany do pojedynczej ścieżki *:8080/dashboard.

My DashboardControllerzbiera wiele informacji z innych usług, aby wyświetlić je na pulpicie nawigacyjnym / stronie przeglądu systemu. Potrzebuję tego pojedynczego kontrolera. Muszę więc zabezpieczyć tylko tę jedną ścieżkę (podstawowa autoryzacja lub filtr roli użytkownika).

EDYCJA nr 2 : Ponieważ wszyscy koncentrują się na 8 parametrach w konstruktorze ... To był przykład z prawdziwego świata - stary kod klienta. Zmieniłem to. Ta sama argumentacja dotyczy mnie dla parametrów 4+.

Chodzi o wstrzyknięcie kodu, a nie o budowę instancji.

dieter
źródło
34
Bardzo brzydki konstruktor z 8 zależnościami jest naprawdę niesamowity, ponieważ jest czerwoną flagą, że coś jest nie tak, klasa ma zbyt wiele zależności i prawdopodobnie narusza zasadę pojedynczej odpowiedzialności i powinna zostać zmieniona. Właściwie to dobrze.
Vojtech Ruzicka
6
@VojtechRuzicka to na pewno nie jest miłe, ale czasami nie da się tego uniknąć.
dieter
4
Powiedziałbym, że zasada 3, nie mówiąc już o 40-50, zależności dla każdej klasy powinna być znakiem, że musisz dokonać refaktoryzacji. Nie ma mowy, żeby klasa z 40 zależnościami trzymała się jednej zasady odpowiedzialności lub zasady otwartej / zamkniętej.
Amin J
4
@AminJ Zasada jest świetna, ale rzeczywistość jest inna. Firma, w której pracuję, istnieje od ponad 20 lat i mamy dużo starego kodu. Refaktoryzacja to dobry pomysł, ale kosztuje. Nie wiem też po co to mówię, ale nie miałem na myśli 40-50 zależności, chodzi mi o 40-50 fasoli, podzespołów, modułów ...
dieter
7
@dit, twoja sytuacja jest wyraźnie taka, w której dług techniczny powoduje, że dokonujesz nieoptymalnych wyborów. Mówiąc własnymi słowami, znajdujesz się w sytuacji, w której na Twój proces decyzyjny duży wpływ ma stary kod mający ponad 20 lat. Rozpoczynając nowy projekt, czy nadal zalecałbyś wstrzyknięcie pola zamiast wstrzyknięcia konstruktora? Może powinieneś umieścić zastrzeżenie w swojej odpowiedzi, aby wskazać, w jakich przypadkach wybrałbyś iniekcję polową.
Umar Farooq Khawaja,
0

Jeszcze jedna uwaga - Vojtech Ruzicka stwierdził, że Wiosna wstrzykuje fasolę na trzy sposoby (odpowiedź z największą liczbą punktów):

  1. Przez konstruktora
  2. Poprzez setery lub inne metody
  3. Poprzez refleksję bezpośrednio w pola

Ta odpowiedź jest ZŁA - ponieważ DLA KAŻDEGO RODZAJU SPRĘŻYNY WTRYSKU WYKORZYSTUJE ODBLASK! Użyj środowiska IDE, ustaw punkt przerwania w programie ustawiającym / konstruktorze i sprawdź.

Może to być kwestia gustu, ale może to być również kwestia przypadku. @dieter zapewnił doskonały przypadek, gdy wtrysk w terenie jest lepszy. Jeśli używasz iniekcji pola w testach integracyjnych, które konfigurują kontekst Springa - argument z testowalnością klasy również jest niepoprawny - chyba że chcesz później pisać o testach do swoich testów integracyjnych;)

wujek.oczko
źródło