Czym jest i co daje wstrzykiwanie zależności?

0

Rozumiem wszystkiwanie zależności jako taki prosty koncept (jak ktos napisał na stackoverflow) i podał taki prosty przykład:

public class A {
  private B b;

  public A() {
    this.b = new B(); // A *depends on* B
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  A a = new A();
  a.DoSomeStuff();
}
public class A {
  private B b;

  public A(B b) { // A now takes its dependencies as arguments
    this.b = b; // look ma, no "new"!
  }

  public void DoSomeStuff() {
    // Do something with B here
  }
}

public static void Main(string[] args) {
  B b = new B(); // B is constructed here instead
  A a = new A(b);
  a.DoSomeStuff();
}

Dzieki temu klasa A może przyjąć jakikolwiek argument B, który będzie tworzony gdzieś tam.

No i teraz pojawia się u mnie pytanie. Dlaczego w EJB/Spring używamy adnotacji?

  1. Czy one dają to samo co powyższy przykład?
  2. Czy dają jeszcze coś więcej?
  3. Czy jak dam w klasie ABC pole z @Inject dla obiektu to gdzieś "pod spodem" wykonuje się konstruktor przyjmujący właśnie ten obiekt?
    I jak dodam kolejny Inject to pod spodem mam wykonuje sie konstruktor przyjmujacy dwa argumenty?
public class ABC {
@Inject
private ObiektX obiektX; 
.
.
.
}
0

Będę wypowiadał się głównie o springu bo najlepiej znam.

D3X napisał(a):

Dlaczego w EJB/Spring używamy adnotacji?

  1. Nie trzeba korzystać z adnotacji, można zależności trzymać w XMLu.
  2. Jeśli już korzystasz z adnotacji to musisz określić, które pola w klasie powinny być wstrzyknięte, a które nie. Kontener DI bez konkretnej deklaracji (a tym są właśnie adnotacje) nie wstrzyknie Ci zależności.
  3. Z punktu widzenia użytkownika jest to wygodniejsze. W dużych projektach łatwiej jest wklepać: "@Autowire @Qualifier(name = "jakastamnazwa")" niż pilnować, żeby kilkadziesiąt obiektów dostało referencję do tego samego obiektu.
  4. Z punktu widzenia użytkownika jest to bezpieczniejsze. Przy starcie aplikacji Spring sprawdza, czy wszystkie wymagane obiekty istnieją. Jeśli nie to dostajesz elegancki komunikat, czego brakuje.
D3X napisał(a):
  1. Czy one dają to samo co powyższy przykład?
  2. Czy dają jeszcze coś więcej?

Dają one to samo co powyższy przykład i jeszcze trochę. Spring pozwala na sprawdzanie zasięgu obiektu. Przykładowo dla aplikacji webowych - można zdefiniować, które obiekty powinny być oddzielne dla każdej sesji, a które powinny być wspólne dla wszystkich.

D3X napisał(a):
  1. Czy jak dam w klasie ABC pole z @Inject dla obiektu to gdzieś "pod spodem" wykonuje się konstruktor przyjmujący właśnie ten obiekt?

Różnie. Czasami do wstrzykiwania zależności korzysta się z refleksji (dostęp bezpośrednio do pól), czasami przez zdefiniowane gettery/settery, czasami przez konstruktory. Być może czasem jakaś inna magia się zadzieje ;)

Samo @Inject nie załatwi Ci wszystkiego, potrzebujesz jeszcze @named.

2

To dlaczego @Inject powstał? W sensie nie możemy używać tego wstrzykiwania, które pokazałem w przykładzie?

Możemy, i do tego to służy. Ale sam @Inject, oderwany od reszty nie wystarczy.

Generalnie z DI sprawa wygląda tak:

  1. Masz jakiś obiekt, który zarządza zależnościami. W Spring'u to jest BeanFactory.

  2. Do tego obiektu przekazujesz:
    a) zbiór klas z informacjami o tym, kiedy je tworzyć
    b) konkretne obiekty, którym należy wstrzyknąć zależności
    c) informacje o tym, jakie obiekty powinny zostać stworzone

  3. Część z tych obiektów może trafić do kontekstu - wspólnej puli obiektów,

Jak to wygląda w praktyce na przykładzie Springa i CDI:
Przykład #1

Mamy dwie klasy:


import javax.inject.Inject;

public class ClassA {

    @Inject
    private ClassB b;

    public boolean bExists(){
        return b != null;
    }
}

public class ClassB {

	private String value = "ClassB";

	public String getValue(){
		return this.value;
	}
}

I teraz:

public class Main {
	public static void main(String[] args){
		ClassA a = new ClassA();
		ClassB b = new ClassB();

		System.out.println(a.bExists());
	}
}

Wypisze false. Dlaczego? Ponieważ same adnotacje nic nie robią. Są one jedynie wskazówkami dla kontenera DI co zrobić - ale w wyżej wymienionym przykładzie nie ma żadnego kontenera DI.

Teraz - tak jak pisałem robię to na przykładzie Springa, tworzę konfigurację i lekko zmieniam beany:

@Configuration
@ComponentScan(basePackages = "examples.injected")
public class Config {
}

@Named
public class ClassA {

    @Inject
    private ClassB b;

    public boolean bExists(){
        return b != null;
    }
}

@Named
public class ClassB {

    private String value = "ClassB";
    
    public String getValue(){
        return value;
    }
}
public class Main {
	public static void main(String[] args){
       		AnnotationConfigApplicationContext ctx =
            		new AnnotationConfigApplicationContext();
        	ctx.register(Config.class);
        	ctx.refresh();

        	ClassA a = ctx.getAutowireCapableBeanFactory().getBean(ClassA.class);
        	System.out.println(a.bExists());

		ClassA a2 = new ClassA();
		System.out.println(a2.bExists());
	}
}

Takie coś wypisze true a potem false.

Co tu się zadziało:

  1. Na podstawie Configu Spring przeskanował wszystkie klasy w pakiecie examples.injected
  2. Wybrał te, które są oznaczone "@named"
  3. Stworzył obiekty
  4. Znalazł @Inject w ClassA więc dodatkowo podpiął obiekt klasy ClassB.

Przy

ClassA a2 = new ClassA();

te kroki się nie zadziały, adnotacji równie dobrze mogłoby nie być.

Adnotacja @Inject powstała po to, żeby różne kontenery DI wiedziały, czego szukać. Wcześniej Spring miał swoje adnotacje, Guice swoje itp.

0

@D3X

  1. Raczej adnotacje wstrzykują przez refleksje a nie konstruktor/settery
  2. Różnica jest taka że kontener IoC SAM wstrzykuje ci zależności! Nie musisz potem miec gdzieś w kodzie magicznego miejsca które tworzy milion obiektów i wstrzykuje je gdzie są potrzebne. Zamiast tego piszesz po prostu w jakimś serwisie @Inject i niczym sie nie przejmujesz
  3. Dodatkowo nie martwisz sie w ogóle tym kiedy i jak tworzone są pewne obiekty. Pomyśl że mozesz mieć obiekty o innym zasięgu, np. takie które żyja zgodnie z Sesją użytkownika. Nowa sesja = nowy obiekt. Ogarniecie tego "ręcznie" byłoby karkołomne.

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