Projektowanie, Programowanie, Codzienność – BeniaminZaborski.com

18 grudnia 2008

Ugryźć Spring.Net – (cz.2) Programowanie aspektowe

Filed under: Ugryźć Spring.Net — Tagi: , , , , , , — Beniamin Zaborski @ 07:03

W tej części zajmiemy się AOP, a raczej tym co ma wspólnego Spring.Net z AOP. Na wstępie odrobina teorii i wyjaśnienia podstawowych terminów. Co to jest AOP?
AOP to skrót od Aspect Oriented Programming czyli po naszemu programowanie aspektowe. Jest to paradygmat programowania, którego główna idea to modularyzacja i enkapsulacja kodu. Autorem całej tej idei jest zespół z firmy Xerox PARC. To oni stworzyli pierwszy i chyba nadal najbardziej popularny aspektowy język programowania – AspectJ.
AOP świetnie uzupełnia podejście OOP czyli Object Oriented Programming. W OOP myślimy o programie jako o hierarchii obiektów, natomiast AOP rozkłada program na tzw. aspekty. Aspekt możemy traktować jako pewne zadanie, które wykonujemy w naszej aplikacji i jest ono rozproszone na wiele obiektów, np. logowanie. Logowanie jest najczęściej przytaczanym przykładem w kontekście programowania aspektowego. Charakter tej operacji idealnie pasuje do paradygmatu aspektowego.

Załóżmy, że mamy w naszej aplikacji sporą grupę obiektów typu Data Access Object i każdy z nich posiada m.in. podstawowe operacje CRUD. Chcemy aby każde wywołanie metody Delete z każdego obiektu DAO było logowane do pliku. Aby się nie napracować zbytnio tu przychodzi nam z pomocą AOP. Zanim przejdziemy do praktycznych przykładów wyjaśnić należy kilka podstawowych terminów związanych z programowaniem aspektowym.

  • Jointpoint (punkt złączenia): dowolne, identyfikowalne miejsce w programie posiadające kontekst, jak: wywołanie metody, wywołanie konstruktora, wyrzucenie wyjątku, itp.
  • Advice (rada): akcja wykonywana przed, po lub zamiast osiągnięcia przez program punktu złączenia. 
  • Pointcut (punkt przekroju): zbiór punktów złączeń dla których powinna się wykonywać wskazana rada.

Odnosząc te terminy do naszego przykładu, to punktem złączenia możemy nazwać metodę Delete, radą – fragment kodu zapisującego informacje do pliku, a punktem przekroju informację np. w postaci wyrażenia regularnego określającego, że rada ma się wykonać we wszystkich obiektach DAO.

Spring.Net posiada wbudowany framework AOP. Oczywiście jeśli nie potrzebujemy go w naszej aplikacji nikt nie wymaga od nas używania go. Trzeba mieć jednak świadomość tego, iż kilka mechanizmów frameworka Spring.Net opiera się na AOP, jak choćby transakcje. Z AOP jest związany jeszcze tzw. proces Weaver czyli tkanie. Polega to na dołączeniu aspektów do oryginalnego kodu. W Spring.Net odbywa się to poprzez generowanie obiektów proxy w trakcie uruchomienia aplikacji (dzięki System.Reflection.Emit jest tworzony odpowiedni kod IL).

Zacznijmy od punktów złączeń odnosząc naszą wiedzę na ten temat do tego co oferuje nam w tym temacie Spring. W Spring-u znajdziemy interfejs Spring.Aop.IPointcut, z którego zainteresuje nas najbardziej property MethodMatcher typu IMethodMatcher.

public interface IMethodMatcher
{
    bool IsRuntime { get; }
     bool Matches(MethodInfo method, Type targetType);
    bool Matches(MethodInfo method, Type targetType, object[] args);
}

 
Metoda Matches(MethodInfo method, Type targetType) jest wywoływana, aby określić czy punkt złączenia będzie osiągnięty/dopasowany na docelowym obiekcie targetType. Takie sprawdzanie będzie wykonane w momencie tworzenia obiektu proxy, aby uniknąć potrzeby weryfikacji przy każdym wywołaniu metody.  Jeśli wystąpi dopasowanie wtedy Matches(MethodInfo method, Type targetType) zwraca true podobnie jak właściwość IsRuntime. Trzy-argumentowa wersja Matches zostanie wywołana na każde wykonanie metody.
Najczęściej stosowaną metodą określania pointcut-ów są wyrażenia regularne. W tym celu Spring dostarcza klasę: Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor. Używając tej klasy możemy dostarczyć listę pattern-ów które określą dopasowanie. Prosty przykład:

<object id=”settersAndAbsquatulatePointcut”
    type=”Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop”>
    <property name=”patterns”>
        <list>
            <value>*Delete</value>
            <value>*Usun</value>
        </list>
    </property>
</object>

Jeśli którekolwiek z tych wyrażeń będzie spełnione wtedy Matches zwróci true – dla metod kończących się na Delete lub Usun. Inne podejście to dopasowanie po atrybucie, np.:

<object id=”cachePointcut” type=”Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop”>
    <property name=”Attribute” value=”Spring.Attributes.CacheAttribute, Spring.Core”/>        
</object>

Takie użycie dopasuje wszystkie metody mające nałożony atrybut Spring.Attributes.CacheAttribute.
Ustaliliśmy już gdzie chcemy wykonać nasz kod, teraz jak określić co chcemy wykonać? Spring.Net dostarcza nam kilka typów rad i odpowiadających im interfejsów :

  • interception around advice – wykonanie w miejsce metody
public interface IMethodInterceptor : IInterceptor
{
    object Invoke(IMethodInvocation invocation);
}

Nasza przykładowa implementacja takiego typu advice-a może wyglądać tak;

public class DebugInterceptor : IMethodInterceptor
{
    public object Invoke(IMethodInvocation invocation)
   {
        Console.WriteLine(„Before: invocation=[{0}]”, invocation);
        object rval = invocation.Proceed();
        Console.WriteLine(„Invocation returned”);
        return rval;
    }
}

Jak widzimy do metody Invoke jest przekazywany parametr invocation, który pozwala nam samemu wykonać właściwą metodę i zwrócić jej wynik.

  • before advice – wykonanie przed wykonaniem metody
public interface IMethodBeforeAdvice : IBeforeAdvice
{
    void Before(MethodInfo method, object[] args, object target);
}

 

  • throws advice – wykonanie przy wyrzucaniu wyjątku
public interface IThrowsAdvice : IAdvice

 

  • after returning advice – wykonanie po wykonaniu metody, przed zwróceniem wyniku
public interface IAfterReturningAdvice : IAdvice
{
  void AfterReturning(object returnValue, MethodBase method, object[] args, object target);
}

Wracając do naszego przykładu najbardziej odpowiedni typ advice to before advice. Zaimplementujmy go zatem:

public class LogujUsuwanieBeforeAdvice : IMethodBeforeAdvice
{
    public void Before(MethodInfo method, object[] args, object target)
    {
        Logger.ToFile(“Usunięto obiekt”, LogLevel.Warning);
       }
}

A tak powinien wyglądać kompletny plik konfiguracyjny do naszego przykładu:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<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;
      <object id=”abonentDAO” type=”BizDev.SimpleSpringAOP.AbonentDAO, BizDev.SimpleSpringAOP”/>
      <object id=”uzytkownikDAO” type=”BizDev.SimpleSpringAOP.UzytkownikDAO, BizDev.SimpleSpringAOP”/>

      <object id=”logujUsuwanieBeforeAdvice” type=”Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop”>
        <property name=”patterns”>
          <list>
            <value>.*Delete</value>
            <value>.*Usun</value>
          </list>
        </property>
        <property name=”advice”>
          <object type=”BizDev.SimpleSpringAOP.LogujUsuwanieBeforeAdvice, BizDev.SimpleSpringAOP”/>
        </property>
      </object>

      <object id=”daos” type=”Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop”>
        <property name=”ObjectNames”>
          <list>
            <value>*DAO</value>
          </list>
        </property>
        <property name=”interceptorNames”>
          <list>
            <value>logujUsuwanieBeforeAdvice</value>
          </list>
        </property>
      </object>

    </objects>
  </spring>
</configuration>

Kilka słów podsumowania tego co widzimy w konfiguracji. Utworzyliśmy własną radę LogujUsuwanieBeforeAdvice wykonującą nasze zadanie tj. logowanie informacji do pliku. Następnie zdefiniowaliśmy pointcut dla tego advice-a określając, że ma się wykonywać dla metod kończących się ciągiem znaków Delete lub Usun. Na końcu zdefiniowaliśmy obiekt typu ObjectNameAutoProxyCreator wskazując nasz advice oraz określając, że ma być wykonany na wszystkich obiektach, których nazwa kończy się na DAO.
       
Teraz fragment kodu aktywujący wywołanie zaaplikowanych advice-ów:
     

IApplicationContext ctx = ContextRegistry.GetContext();
IDAO adao = (IDAO)ctx.GetObject(„abonentDAO”);
adao.Delete();
adao.SafeDelete();
       
IDAO udao = (IDAO)ctx.GetObject(„uzytkownikDAO”);
udao.Delete();
udao.SafeDelete();

      
A więc zadanie, które sobie założyliśmy na początku zostało zrealizowane bez modyfikacji kodu klas Data Access Object. Warto wspomnieć, iż to wszystko co skonfigurowaliśmy w pliku konfiguracyjnym można zrobić w kodzie bez udziału kontenera IoC, np.:
     

ProxyFactory factory = new ProxyFactory(new AbonentDAO());
factory.AddAdvice(new LogujUsuwanieBeforeAdvice());
IDAO dao = (IDAO)factory.GetProxy();
dao.Delete();

      
Do tego artykułu wybrałem przypadek nie do końca najbardziej trywialny od strony konfiguracji, ale jakże praktyczny. Oczywiście advice-y można aplikować jeszcze na kilka innych sposobów, ale po szczegóły odsyłam wszystkich zainteresowanych  do dokumentacji projektu. W kolejnej części napiszę o mechanizmach dostępu do danych w Spring.Net.

17 grudnia 2008

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

Filed under: Ugryźć Spring.Net — Tagi: , , , , , , — 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.

Ugryźć Spring.Net – (cz.0) Wprowadzenie

Filed under: Ugryźć Spring.Net — Tagi: , , , , , , — Beniamin Zaborski @ 06:59

To pierwszy artykuł z serii na moim blogu poświęcony Spring.NET. Jak na pierwszy przystało należy się kilka słów wstępu. Co to takiego właściwie jest ten Spring.Net?
Spring.Net jest frameworkiem wspomagającym budowanie aplikacji enterprise w środowisku .NET. Dostarcza kompleksowe rozwiązania do budowania aplikacji biznesowych. Sam Spring.NET bazuje na java-owskiej wersji Spring-a, jednakże nie jest jego bezpośrednim portem na platformę .NET, a raczej czerpie z filozofii oraz architektury swojego starszego brata.
Coraz bardziej przyzwyczajamy się, iż wiele świetnych rozwiązań ze świata Javy zostało z sukcesem przeniesionych na platformę .NET. Dobrym przykładem obok Spring.NET może być także NHibernate. Jeszcze kilka ciekawych zostało, czekamy … ;-).
Zadaniem tego artykułu nie jest omawianie architektury aplikacji enterprise, bo z takową raczej każdy kto poważnie interesuje się Spring.NET jest zaznajomiony, ale nie wypada nie wspomnieć o kliku podstawowych sprawach. Dobrze zaprojektowane aplikacje enterprise zbudowane są z fizycznych i logicznych warstw. Praktycznie w każdym modelu powinniśmy być w stanie wyodrębnić logiczne warstwy: dostępu do danych, logiki biznesowej, prezentacji. W zależności od przyjętej architektury tych warstw może być więcej, niektóre z nich podlegają dodatkowemu podziałowi. Spring.Net dostarcza wsparcia praktycznie dla każdej z wymienionych warstw aplikacji. W serii moich artykułów postaram się przedstawić większość funkcjonalności jakie daje nam Spring.
Spring.Net posiada budowę modułową i teraz dwa słowa o każdym z modułów, które w kolejnych częściach serii zostaną przybliżone.

  • Spring.Core: samo serce frameworka, obsługa konfiguracji aplikacji, kontener Dependency Injection, a także walidacja, mechanizm wyrażeń, obsługa wątków, zasobów, itp.
  • Spring.Aop: moduł obsługi tzw. Aspect-Oriented Programming. Dodać należy, iż nawet jeśli nie wykorzystujemy bezpośrednio AOP we własnych aplikacjach, to wiele elementów Spring-a bazuje na AOP, jak choćby transakcje.
  • Spring.Data: zestaw klas ułatwiających nam dostęp do danych poprzez ADO.NET.
  • Spring.Data.NHibernate – zestaw klas ułatwających nam dostęp do danych dzięki integracji z mechanizmem O/R Mappingu Nhibernate.
  • Spring.Services: moduł dający możliwość publikowania serwisów dla aplikacji rozproszonych wykorzystując przy tym dobrze znane technologie jak: .Net Remoting, Enterprise Services, Web Services, czy w najnowszej wersji nawet WCF.
  • Spring.Web, Spring.Web.Extensions: moduły wsparcia dla webowych aplikacji ASP.NET, tj. m.in. bindowanie, walidacja, page/control/module/provider.
  • Spring.Testing.NUnit: integracja testów jednostkowych z Nunit.

Jak widać framework może nam pomóc dość kompleksowo, choć oczywiście możemy skorzystać tylko z wybranych elementów Spring-a.

Oto pierwszy wpis na blogu i lekkie kilka słów wprowadzenia – na początek … wystarczy. W kolejnej części przybliżę ideę kontenera IoC w Spring.Net.

Blog na WordPress.com.