Zrozumienie wstrzykiwania zależności

0

Rozumiem, że @Autowired służy do tego, żeby mi samo stworzyło i znalazło odpowiedni obiekt, ale do czego wstrzykiwanie przez konstruktor? Jak i tak muszę stworzyć odpowiedni Bean i tam nadać wartości, a można to zrobić bezpośrednio w klasie? Jak nie nadam wartości odpowiednim obiektom w konstruktorze to po co mi coś takiego?;

@Autowired
   public Foo(Bar bar) {
        this.bar = bar;
    }

Jak to moge potem sensownie użyć? Co jak potrzebuje inne wartości niż podane przy @Bean?

0
  1. Nie stosuj @Autowired, @Component (i reszty z tej rodziny)
  2. Twoja klasa Foo zależy od jakiegoś obiektu Bar. Załóżmy, że jest to jakiś interfejs serwisu i masz jego implementacje konkretną i testową lub jeszcze więcej (np TimeService gdzie raz używasz spreparowanego clocka dla testów a raz defaultowego). Gdybyś sobie w konstruktorze po prostu utworzył:
public Foo() {
        this.timeService = TimeService.getDefault();
}

To jak potem na testach chcąc przetestować klasę Foo dostarczysz time service ze spreparowanym clockiem ?

Dlaczego robisz tak:

public Foo(TimeService timeService) {
        this.timeService = timeService;
}

i potem w kodzie dostarczasz albo to albo to:

new Foo(TimeService.getDefault());

lub na testy

new Foo(TimeService.withPreparedClock(anyPreparedClock));

Chyba, że ten Bar to jest jakiś prosty pomocniczy obiekcik z jedną implementacją i bez zewnętrznych zależności to spoko, nie boję się new i go tworzę w konstruktorze. Gdzieś oglądałem ostatnio autorstwa @jarekr000000 konfe o tym. On się nie boi 'new' :D

A jeśli dynamicznie chcesz wstrzykiwać różne implementacje bo jest to np jakaś polityka naliczania rabatów to wstrzykuj to do metody, a nie do klasy.

0
  1. Jest kolosalna różnica między wsrzyknięciem obieku (np. kontruktorem) a utworzeniem go samemu. W przypadku 1 możesz współdzielić taki obiekt! Bardzo często chciałbyś jednak używać jednego obiektu w kilku miejscach. Pomśl że ten obiekt to może być jakis cache albo serwis.
  2. Wstrzykiwać przez kontener IoC możesz np. listę wszystkich implementacji jakiegoś interfejsu, co jest dość wygodne kiedy masz jakieś dynamiczne pluginy w aplikacji.
  3. Kontener pozwala nie przejmować się "składaniem" obiektów, po prostu deklarujesz "chce dostęp do serwisu X" i tyle. Można to samo zrobić ręcznie, nic nie stoi na przeszkodzie. Kontenery IoC to ma być jakieśtam ułatwienie przy składaniu aplikacji, ale nie jest to jakaś magiczna technologia lecząca raka. Jak masz dobrze zrobioną aplikację to mógłbyś wyrzucić kontener i zrobić sobie klasę z main() i tam za pomocą new poskładać wszystko. Tylko po co? ;)
0
  1. Współdzielenie obiektu na pewno może być przydatne, ale dlaczego nie używać adnotacji typu @Autowired, @Component?
  2. Zastanawiam się czy konstruktor z przykład http://www.baeldung.com/constructor-injection-in-spring jest jednorazowy? Czy zawsze gdy chcę go użyć musze mieć stworzony jakiś @Bean? Później konstruktor takiej klasy w której mam @Autowired na konstruktorze wstrzykuje przez pole np. @Autowired Car car , a mógłbym to zrobić wszystko przez new i było by mniej pisania?
1

Wstrzykiwanie zależności polega na tym, że najpierw podajesz obiektowi jego zależność, a potem wykonujesz na nim metodę, która tą zależność wykorzystuje. Wstrzykiwanie może być przez konstruktor (lepiej, bo łatwiej utrzymać spójność obiektu) lub przez setter (gorzej, bo każdy korzystający z klasy musi pamiętać o odpaleniu settera).

Wstrzykiwanie przez konstruktor:

// tej klasie wstrzykujemy zależność przez konstruktor, a potem korzystamy z niej w metodzie zróbKromkę
class Klasa { 
  final ChlebakService zależność;
  public Klasa(ChlebakService zależność) {
    this.zależność = zależność;
  }
  Kromka zróbKromkę() {
    return zależność.getChleb().pokrój();
  }
}

Występuje w wersji klasycznej ręcznej (z użyciem słówka new) jak i magicznej (przy użyciu adnotacji Autowired, Inject i tym podobnych).

Wstrzykiwanie przez setter:

class Klasa { 
  ChlebakService zależność = null;
  void setZależność(ChlebakService zależność) {
    this.zależność = zależność;
  }
  Kromka zróbKromkę() {
    return zależność.getChleb().pokrój();
  }
}

Brak wstrzykiwania - tworzenie zależności w konstruktorze. Konstruktor ma efekt uboczny (tworzenie nowego obiektu, potencjalnie robiącego I/O lub dużo obliczeń) co jest złe, bo utrudnia testowanie:

class Klasa { 
  final ChlebakService zależność = new ChlebakService();
  Kromka zróbKromkę() {
    return zależność.getChleb().pokrój();
  }
}

Brak wstrzykiwania - zależnośc podajemy razem z jej wykorzystaniem, nie jest ona nigdzie w obiekcie zapisywana:

class Klasa { 
  Kromka zróbKromkę(ChlebakService zależność) {
    return zależność.getChleb().pokrój();
  }
}

Polecam najpierw przećwiczenie opcji pierwszej w wersji klasycznej, czyli wstrzykiwania przez konstruktor z użyciem słówka new. Potem można przejść do opcji magicznej jeśli koledzy nalegają, ale ja bym się przed tym bronił.

0
Wibowit napisał(a):

Wstrzykiwanie zależności polega na tym, że najpierw podajesz obiektowi jego zależność, a potem wykonujesz na nim metodę, która tą zależność wykorzystuje. Wstrzykiwanie może być przez konstruktor (lepiej, bo łatwiej utrzymać spójność obiektu) lub przez setter (gorzej, bo każdy korzystający z klasy musi pamiętać o odpaleniu settera).

Wstrzykiwanie przez konstruktor:

// tej klasie wstrzykujemy zależność przez konstruktor, a potem korzystamy z niej w metodzie zróbKromkę
class Klasa { 
  final ChlebakService zależność;
  public Klasa(ChlebakService zależność) {
    this.zależność = zależność;
  }
  Kromka zróbKromkę() {
    return zależność.getChleb().pokrój();
  }
}

ale potem jak wywołuje Klasa(zależność) muszę stworzyć new ChlebakService?

0

No z powietrza się ten chlebak nie weźmie. W którymś momencie trzeba go stworzyć.

0

Obiekt Klasa też będę musiał gdzieś stworzyć za pomocą new? Wstrzykiwanie zależności powinno się stosować tylko w określonych przypadkach czy wszędzie? Tam gdzie będę ostatecznie tworzył obiekt new Klasa też mogę użyć DI i tak w kółko, a kodu znacząco przybędzie, a czy mój program będzie ostatecznie przez to wydajniejszy?

1

Abstrakcyjny przykład ręcznego wstrzykiwania zależności: Tworzenie nadmiarowych interfejsów.

Wstrzykiwanie zależności generalnie nie ma specjalnie związku z wydajnością. Nie będzie ani wolniej ani szybciej - new jest po prostu odpalane poza konstruktorem i tyle, jak miałoby to mieć wpływ na wydajność? Główną zaletą wstrzykiwania zależności jest ułatwienie testowania. Wstrzykiwanie zależności wiąże się w dużej mierze z wycięciem efektów ubocznych z konstruktora - konstruktor jedyne co robi to przepisuje pola. Z tego względu samo tworzenie obiektów jest tanie i bezpieczne (nie będziesz miał nagle sytuacji, że w konstruktorze łączysz się z bazą). W testach pożądane (a raczej wymagane) jest tworzenie wielu instancji testowanej klasy po to by testy były od siebie odizolowane. Dzięki wstrzykiwaniu zależności możesz wstrzyknąć testowe implementacje zależności zamiast "prawdziwych" (produkcyjnych). Tzn w kodzie produkcyjnym masz new Klasa(prawdziwyChlebak), a w testach masz new Klasa(testowyChlebak). Przykładowo: prawdziwy chlebak łączy się z bazką więc jest kosztowny do stawiania, ale testowy chlebak działa na kolekcji w pamięci i można go postawić w ułamku milisekundy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1