Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

17 Grudzień 2008

Ugryźć Spring.Net – (cz.1) Kontener IoC

Filed under: Ugryźć Spring.Net — Tags: , , , , , , — Beniamin Zaborski @ 07:00

W poprzedniej części wprowadzającej zdradziłem temat, a więc zajmiemy się kontenerem IoC. Napisałem także, że z frameworka Sping.Net można skorzystać kompleksowo lub użyć tylko jego wybranych elementów. Z czego byśmy jednak nie skorzystali to potrzebna nam będzie podstawowa wiedza o skonfigurowaniu Springa i o kontenerze Dependency Injection / IoC.
Zanim zaczniemy musimy wiedzieć co to takiego jest Dependency Injection? Jest to jeden z wzorców projektowych, który jak podaje Wikipedia można zdefiniować tak:

„Wstrzykiwanie zależności (ang. Dependency Injection, DI) – wzorzec projektowy i wzorzec architektury oprogramowania polegający na usuwaniu bezpośrednich zależności pomiędzy komponentami na rzecz architektury typu plug-in. Jest on często utożsamiany z Odwróceniem sterowania (ang. Inversion of Control, IOC), jakkolwiek z technicznego punktu widzenia DI jest jedną ze szczególnych (obecnie najpopularniejszą) realizacji paradygmatu IoC.”

Oryginalna definicję Martina Fowlera można znaleźć tutaj: http://www.martinfowler.com/articles/injection.html#InversionOfControl

Jeśli nie do końca się to jeszcze rozjaśniło to tak własnymi słowami można DI opisać jako czarodziejskie narzędzie, które na podstawie sporządzonej konfiguracji (np. w XML-u) wstrzykuje instancje do naszych klas. To czarodziejskie narzędzie to właśnie kontener Depenedency Injection / IoC. W dalszej części będę tych dwu terminów używał zamiennie choć podkreślić należy subtelne różnice między nimi, które zresztą wyjaśnia Martin Fowler.

Teraz możemy już przejść do samego Spring-a. Interfejsem, który na początku powinien nas najbardziej zainteresować jest IObjectFactory lub też jego bardziej wypasiony brat IApplicationContext. Jak podaje dokumentacja ten drugi ma więcej możliwości i jest bardziej enterprise-centric. IObjectFactory odpowiedzialny jest za powoływanie, konfigurowanie oraz zarządzanie obiektami wewnątrz kontenera IoC. Istnieje kilka implementacji tego interfejsu, jednakże najbardziej zainteresuje nas – przynajmniej na początku – XmlObjectFactory. Ta implementacja bazuje na metadanych dostarczanych do kontenera IoC z XML-a, np. umieszczonego w pliku konfiguracyjnym naszej aplikacji.
Kontener IoC potrafi wstrzykiwać zależności do klas naszej aplikacji na podstawie metadanych. W ten sposób powstaje w pełni skonfigurowana i używalna instancja wymaganej klasy. Metadane mogą pochodzić z różnych źródeł, jednak najbardziej popularne jest umieszczanie ich w XML-u. Takim właśnie przypadkiem zajmiemy się w artykule.

Rozpatrzmy pewien przykład. Daruję sobie przykłady w stylu Hello World i spróbujmy czegoś bardziej praktycznego. Przypadek bardzo często spotykany w aplikacjach biznesowych –  serwis potrzebuje instancji obiektu dającego dostęp do danych.
Załóżmy, że tworzymy serwis AbonenciService, który posiada jedną metodę pobierająca listę abonentów. Nasz serwis pobiera dane z bazy danych przy pomocy metody PobierzAbonentow() obiektu AbonentDAO (Data Access Object).

public class AbonenciService : IAbonenciService
{
    #region Data Access Objects    private AbonentDAO abonentDAO;
    public AbonentDAO AbonentDAO
    {
        get { return abonentDAO; }
        set { abonentDAO = value; }
    }
    
  #endregion    

    public IList<AbonentDTO> PobierzListeAbonentow()
    {
        List<AbonentDTO> listaAbonentow = new List<AbonentDTO>();
        foreach(Abonent abonent in AbonentDAO.PobierzAbonentow())
        {
            listaAbonentow.Add(AbonentConverter.ToAbonentDTO());
        }
        return listaAbonentow;
    }
}

Metoda serwisu PobierzListeAbonentow() wywołuje metodę obiektu AbonentDAO by pobrać listę encji z bazy danych. Nie trudno zauważyć, iż nigdzie jawnie nie tworzymy instancji obiektu  AbonentDAO. Powołaniem instancji tej klasy oraz wstrzyknięciem jej do naszego serwisu zajmie się właśnie kontener IoC. Oczywiście musimy mu w tym pomóc, czyli dokonać odpowiedniej konfiguracji. Najbardziej pożądaną metodą jest użycie sekcji konfiguracyjnej Springa w standardowym pliku konfiguracyjnym (app.config czy web.config).

<configuration>
  <configSections>
    <sectionGroup name=”spring”>
      <section name=”context” type=”Spring.Context.Support.ContextHandler, Spring.Core”/>
      <section name=”objects” type=”Spring.Context.Support.DefaultSectionHandler, Spring.Core” />
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri=”config://spring/objects”/>
    </context>
    <objects xmlns=”http://www.springframework.net”&gt;
        …
    </objects>
  </spring>
</configuration>

Tak wygląda najprostszy plik konfiguracyjny zaprzęgający Spring-a do pracy. Na tym etapie jedyne co nas będzie interesować to sekcja <objects>. To w tym miejscu definiujemy obiekty naszej aplikacji. Spróbujmy zatem wstrzyknąć wspomnianą instancję klasy AbonentDAO do serwisu  AbonenciService. Sekcja <objects> powinna wyglądać teraz tak:


<object id=”abonentDAO” type=”BizDev.SpringSample.Model.AbonentDAO, BizDev.SpringSample.Model”></object>

<object id=”abonenciService” type=”BizDev.SpringSample.Model.AbonenciService, BizDev.SpringSample.Model”>
  <property name=”AbonentDAO” ref=”abonentDAO”/>
</object>

W pierwszym tagu <object> definiujemy nasz obiekt typu  AbonentDAO i nadajemy mu identyfikator w kontenerze – abonentDAO. Drugi <object> to nasz serwis typu AbonenciService. Tutaj najbardziej zainteresuje nas linia: <property name=”AbonentDAO” ref=”abonentDAO”/>, która mówi że do property o nazwie  AbonentDAO ma zostać przypisany obiekt, który wcześniej zdefiniowaliśmy pod identyfikatorem abonentDAO. Resztą zajmie się kontener IoC Spring-a. Proste prawda?
Owszem proste, ale został tu przedstawiony najbardziej trywialny przypadek. Możliwości konfiguracji kontenera IoC są znacznie większe. Nie zawsze pożądane jest tworzenie obiektu przez przypisanie do property. Spring daje nam inne możliwości, jak choćby przekazanie obiektu do konstruktora czy utworzenie instancji poprzez metodę fabrykującą.
Wyjaśnienia wymaga jeszcze ref, które znalazło się w pliku konfiguracyjnym. Wskazuje nam na referencję innego obiektu z kontenera. A co gdy chcemy wstrzyknąć bezpośrednio wartość, a nie referencję do innego obiektu? Załóżmy, że nasza klasa serwisu posiada property:
priate int maxCount;
public int MaxCount
{
    get { return maxCount; }
    set { maxCount = value; }
}

Ustawienie wartości MaxCount również możemy zlecić kontenerowi DI, dodając do definicji naszego serwisu w pliku konfiguracyjnym linię: <property name=”MaxCount” value=”1000″/>

Wróćmy teraz do wspomnianych przeze mnie dwóch innych metod wstrzykiwania zależności. Powiedzmy, że klasa serwisu AbonenciService posiada konstruktor:

public AbonenciService(int maxCount)
{
    this.maxCount = maxCount;
}

Ustawiamy wartość MaxCount poprzez konstruktor dodając do definicji klasy AbonenciService  w pliku konfiguracyjnym linię: <constructor-arg index=”0″ value=”2000″/> lub po nazwie parametru konstruktora: <constructor-arg name=”maxCount” value=”2000″/>
A co w sytuacji, gdy powołaniem do życia naszego obiektu zajmuje się jakaś metoda fabrykująca?  Takie podejście również jest dozwolone w Spring-u.
Załóżmy, że nasza klasa AbonentDAO posiada statyczną metodę fabrykującą:

public static AbonentDAO CreateInstance()
{
    return new AbonentDAO();
}

Nasz obiekt AbonentDAO może być wytworzony właśnie za pomocą takiej metody fabrykującej. Konfiguracja AbonentDAO wtedy wymaga dodania do definicji <object>: factory-method=”CreateInstance”. W sytuacji, gdy fabrykowaniem zajmuje się zewnętrzna klasa: factory-method=”CreateInstance” factory-object=”daoFactory”.
Mamy serwis, mamy dao teraz czas na fragment kodu, który coś z tym wszystkim zrobi. Pierwsze i w zasadzie jedyne do czego jesteśmy zobowiązani to zainicjowanie kontenera IoC w naszej aplikacji. Możemy tego dokonać np. takim fragmentem kodu:

IApplicationContext ctx = ContextRegistry.GetContext();

Kontener gotowy do pracy, a więc pobieramy z niego w pełni skonfigurowany i gotowy do działania nasz serwis:

AbonenciService abonenciService = (AbonenciService)ctx.GetObject(”abonenciService”);
IList<AbonentDTO> listaAbonentow = abonenciService.PobierzListeAbonentow();

Zrobione! I nie bolało bardzo ;-). Nie mogę nie zaznaczyć, iż przedstawiłem tu podstawy konfiguracji kontenera IoC w Spring.Net. Po szczegóły odsyłam do wyczerpującej dokumentacji projektu. W kolejnej części opowiem o Spring.AOP.

3 komentarzy »

  1. Fajny opis kontenera IoC ze Spring-a, właśnie czegoś takiego szukałem żeby porównać z innymi kontenerami. Tylko jedna uwaga: „Drugi to nasz serwis typu AbonenciService…”, nigdzie nie widze tam drugiego object-a…

    Komentarz - autor: dmusial — 18 Październik 2009 @ 11:01

    • Niestety nie po raz pierwszy mam problem z treścią moich postów na blogu działającym na silniku WordPress. Oczywiście nie taka była oryginalna treść posta. Poprawię to jak tylko będę mógł najszybciej. Dzięki za zwrócenie uwagi.

      Komentarz - autor: Beniamin Zaborski — 18 Październik 2009 @ 13:05

  2. Sam używam wordpress-a i czasem miewam podobne problemy zwłaszcza jak wrzucam w treść coś co wygląda jak tag html ;) pozdrawiam

    Komentarz - autor: dmusial — 18 Październik 2009 @ 19:59


RSS feed for comments on this post. TrackBack URI

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

Stwórz darmową stronę albo bloga na WordPress.com.

%d bloggers like this: