Mechanizm wstrzykiwania zależności

Mechanizm wstrzykiwania zależności platformy AgE wykorzystuje kontener IoC dostarczony przez technologię PicoContainer. W celu dostoswania dziłania kontenera Pico, należy zaimplementować własny adapter.

Rysunek: Implementacja mechanizmu wstrzykiwania zależności

Rysunek pokazuje diagram klas odpowiedzialnych za wstrzykiwanie zależności. Głównym elementem jest klasa CoreComponentAdapter, która rozszerza abstrakcyjną klasę AbstractComponentAdapter. Dzięki temu może zostać wykorzystana przez kontener Pico do dostarczania instancji komponentów. Metoda getComponentInstance jest wywoływana przez kontener w przypadku, kiedy do kontenera przyszło żądanie uzyskania referencji do danego komponentu. Kontener zawiera dwie metody:getComponentInstance(String) i getComponentInstanceOfType(Class<?>), które pełnią rolę zewnętrznego interfejsu dla klienta. Umożliwiają one dostarczenie instancji komponentu identyfikowanego po zarejestrowanej wcześniej nazwie lub po jego typie.

Adapter deleguje proces tworzenia instancji komponentu do jego definicji, za pomocą metody createInstance. A zatem odpowiedzialność utworzenia komponentu spoczywa na jego definicji. W przypadku klasy ComponentDefinition, instancjonowanie obiektu odbywa się na podstawie informacji opisujących argumenty konstruktorów oraz inicjalizatory właściwości (Property-Initilizers). Argumenty konstruktorów reprezentowane są przez listę obiektów typu IValueProvider, odpowiedzialnych za dostarczenie wartości, które reprezentują. Używane są podczas wstrzykiwania przez konstruktor. Inicjalizatory właściwości wykorzystywane są podczas wstrzykiwania za pomocą adnotowanych metod lub pól. Każdy obiekt klasy PropertyInitializer zawiera nazwę właściwości oraz jej wartość, która ma zostać wstrzyknięta podczas inicjalizacji, reprezentowaną przez obiekt typu IValueProvider{}.

Należy zauważyć, że z poziomu definicji obiekty CoreComponentAdapter są widoczne przez interfejs IComponentInstanceProvider. Takie rozwiązanie uniezależnia model konfiguracji od konkretnej technologii komponentowej.

Interfejs IComponentInstanceProvider zawiera trzy metody odpowiedzialne za dostarczanie instancji komponentów. Metoda getInstance(String) zwraca obiekt identyfikowany po nazwie zarejestrowanej w kontenerze. W przypadku nieznalezienia obiektu o podanej nazwie zwracana jest wartość null. Metoda getInstance(Class<?>) wyszykuje w kontenerze IoC implementację typu podanego jako parametr. Aby taka instancja mogła zostać dostarczona, musi być zarejestrowana dokładnie jedna implementacja żądanego typu. W przeciwnym wypadku zostanie zwrócona wartość null. Ostatnia bezparametrowa metoda, getInstance(), odpowiedzialna jest za dostarczenie obiektu, którego definicja jest skojarzona z instancją IComponentInstanceProvider. Metoda ta wykorzystywana jest na przykład podczas klonowania agentów. Agent wysyła żądanie utworzenia swojej kopii, która następnie zostanie włączona do struktury agentów.

Wstrzykiwanie przez konstruktor

Rysunek przedstawia diagram sekwencji tworzenia instancji komponentu za pomocą wstrzykiwania przez konstruktor{}(Rysunek przedstawia tylko fragment sekwencji zaczynający się od obiektu typu CoreComponentAdapter. Jak opisano wcześniej, klient kieruje żądanie uzyskania komponentu do kontenera IoC, który to deleguje żądanie do obiektu CoreComponentAdapter. Obiekt CoreComponentAdapter deleguje instancjonowanie komponentu do przechowywanej definicji, przekazują wskazanie na siebie (przez interfejs IComponentInstanceProvider). Definicja komponentu, na podstawie listy constructorArguments, tworzy wszystkie obiekty (lub wartości proste) potrzebne do inicjalizacji komponentu. Za ich utworzenie odpowiedzialne są implementacje interfejsu IValueProvider|ivalueprovider.html.

Jeśli pobieranie wszystkich zależności przebiegnie poprawnie, mechanizm wyszukuje odpowiedni konstruktor. Konstruktor musi być publiczny oraz posiadać listę parametrów formalnych odpowiadającą dokładnie typom zawartym w tablicy argumentTypes, w kolejności zapisanej w tablicy. Nowy obiekt reprezentujący komponent jest tworzony za pomocą znalezionego konstruktora.

Rysunek: Diagram sekwencji procesu wstrzykiwania przez konstruktor

Wstrzykiwanie za pomocą adnotowanych pól lub metod

Rysunek przedstawia diagram sekwencji tworzenia instancji komponentu z wykorzystaniem wstrzykiwania za pomocą adnotowanych pól lub metod. Pierwszym krokiem jest stworzenie nowego obiektu za pomocą domyślnego konstruktora lub, w przypadku mieszanego wstrzykiwania, otrzymanie obiektu po zakończonym procesie wstrzykiwania przez konstruktor. Klasa tworzonego obiekt musi implementować interfejs IPropertyContainer. Wykorzystując mechanizm właściwości obiektów, z obiektu pobierane są wszystkie właściwości (obiekty typu Property). Mechanizm ten wykorzystywany jest również do ustawiania wartości właściwości (metoda Property.setValue(Object)). W zależności od tego czy właściwości będą implementowane przez klasę ClassFieldProperty lub ClassGetterSetterProperty, wstrzykiwanie będzie odbywało się za pomocą adnotowanych pól lub metod.

Jeśli dla danej właściwości istnieje odpowiedni obiekt typu PropertyInitializer, to właśnie z niego pobierana jest wartość do wstrzyknięcia. Zawiera on obiekt IValueProvider, który dostarcza odpowiednią referencję lub wartość prostą.

W przeciwnym wypadku rozpoczyna działanie mechanizm auto-wiring, który pobiera z właściwości jej typ i próbuje pobrać z IComponentInstanceProvider pasującą referencję. Warunkiem pobrania obiektu jest istnienie w kontenerze IoC dokładnie jednej implementacji szukanego typu. Mechanizm auto-wiring działa wyłącznie dla typów złożonych.

Rysunek: Diagram sekwencji procesu wstrzykiwania za pomocą adnotowanych pól lub metod