27 maja 2010

Rozdział 2. Koncepcje ze specyfikacji Java EE 6 Contexts and Dependency Injection (CDI)

Ciekaw jestem, jaki też sposób na poznanie specyfikacji Java EE 6 wybrały osoby zajmujące się nią. W jakiś przedziwny dla mnie sposób, mnie wzięło na poznanie specyfikacji od podstaw, czyli wziąłem się za JSR-299 Context and Dependency Injection for the Java EE platform i z każdym dniem idzie sprawniej, ale nie tak, jakbym sobie tego życzył. Najwyraźniej należało zacząć od początku, czyli zabrać się za samą specyfikację Java EE 6 - JSR 316 Java Platform, Enterprise Edition 6 (Java EE 6) Specification. Pewnie dlatego, że byłoby za prosto, zabrałem się za CDI :)

Tym razem streszczę mądrości z rozdziału 2. Concepts.

Zacznę od pytania: Co różni instancję klasy stworzoną przez new, a tymi, przekazanymi przez kontener? Odpowiedzi nie należy się spodziewać (chyba że na wyraźne życzenie), bo powinna nasunąć się m.in. po lekturze specyfikacji CDI, czy tym streszczeniu.

Ziarna i ich typy


Komponent Java EE jest ziarnem (ang. bean), jeśli cykl rozwojowy jego instancji jest zarządzany przez kontener zgodnie z zasadami specyfikacji CDI.

Ziarno może posiadać dodatkowe metadane opisujące jego zachowanie w systemie.

Instancje ziarna nazywane są kontekstowymi instancjami ziarna.

Możliwe jest przekazywanie kontekstowych instancji ziaren do innych (w ramach tego samego kontekstu). Wspomniane przekazywanie to nic innego jak wstrzeliwanie zależności (ang. dependency injection) - jedna z form Inversion of Control (IoC).

Ziarna kontekstowe mogą być wykorzystywane w wyrażeniach Unified Expression Language (Unified EL).

Ziarno opisywane jest przez:
  • niepusty zbiór typów
  • niepusty zbiór kwalifikatorów
  • zasięg
  • (opcjonalnie) nazwy EL
  • zbioru związanych interceptorów
  • implementacji
Rolą programisty JEE6 jest stworzenie implementacji ziarna w Javie i opatrzenie jej odpowiednimi adnotacjami lub też poleganie na domyślnych właściwościach.

Zwykła klasa javowa - POJO - jest ziarnem, ale może też nim być ziarno zarządzane w JavaServer Faces (JSF) czy ziarno Enterprise JavaBeans (EJB). To jest główna idea tej specyfikacji, jak opisano w specyfikacji Java EE - rozdział EE.6.30 Contexts and Dependency Injection for the Java EE Platform 1.0 Requirements (strona 163):

CDI (JSR-299) definiuje zbiór kontekstowych usług, dostarczanych przez kontenery Java EE, których celem jest uproszczenie aplikacji, które korzystają z technologii warstwy webowej i biznesowej.

Tymi usługami kontekstowymi mogą być wstrzeliwanie zależności, metody zwrotne cykli rozwojowych, interceptory czy dekoratory.

Rolą kontenera jest automagiczne zarządzanie (tworzenie i niszczenie) instancjami, związywanie ich z odpowiednim kontekstem, rozwiązywanie nazw po typie i kwalifikatorach lub samej nazwie EL, obsługa metod zwrotnych (a w zasadzie ich wywoływanie w odpowiednim momencie), przechwytywanie wykonywania metod przez interceptory i dekoratory oraz obsługa zdarzeń.

Typ ziarna to zbiór typów widocznych dla klienta. Typ zawsze zawiera java.lang.Object. Dla EJB typami będą interfejsy biznesowe i Object.

Ograniczenie zbioru typów ziarna możliwe jest przez adnotację @javax.enterprise.inject.Typed, której wartość przyjmuje tablicę typów (i zawsze z dodatkowym Object - bez względu, czy jest wymieniony, czy nie).

Typ wyznacza możliwości ziarna i klient może rzutować instancję ziarna kontekstowego jedynie na typy widoczne dla niego.

Kwalifikatory


Kwalifikator pozwala na przypisanie dodatkowych informacji (metadanych) do typu ziarna. Kwalifikator reprezentowany jest przez adnotację, którą tworzy programista. Typ może posiadać wiele kwalifikatorów, które tym samym specjalizują go (zawężają miejsca, w których może być wstrzeliwany przez konieczność wypisania wszystkich kwalifikatorów).

Punkt/miejsce wstrzeliwania to miejsce w dowolnej klasie, w którym dojdzie do wstrzeliwania zależności. Może być opisane przez wiele kwalifikatorów, co jednocześnie zawęża możliwe ziarna.

Wyznaczenie instancji ziarna, która będzie wstrzelona, następuje przez porównanie typów i kwalifikatorów ziarna. W ten sposób możliwe jest udostępnienie różnych implementacji tego samego interfejsu i przez różne kwalifikatory wskazanie ich różnic (zamiast korzystać z ich typów implementacji). Jedynie zgodność typu i wszystkich kwalifikatorów punktu wstrzeliwania umożliwia przypisanie instancji ziarna.

Kwalifikatory mogą również służyć do filtrowania zdarzeń przez klasy nasłuchujące na zdarzenie.

Wbudowane kwalifikatory należą do pakietu javax.enterprise.inject wraz z @javax.inject.Named.

Każde ziarno ma domyślnie przypisany kwalifikator @Any (poza ziarnami stworzonymi przez @New).

Jeśli ziarno nie ma jawnie przypisanego kwalifikatora, poza @Named, ziarno ma dodatkowy kwalifikator @Default zwany kwalifikatorem domyślnym.

Kwalifikator to adnotacja Javy zdefiniowana jako @Target({METHOD, FIELD, PARAMETER, TYPE}) z @Retention(RUNTIME), ale także zawiera meta-adnotację @javax.inject.Qualifier i zwykle @java.lang.annotation.Documented. Kwalifikator może posiadać atrybuty.

Kwalifikatory ziarna są wyznaczone przez udekorowanie klasy ziarna lub metody/pola producenta typem kwalifikatora.

Kwalifikatory mogą być przypisane do metod, pól, parametrów i typu (zgodnie z deklaracją @Target), a przekładając to na język CDI będą to m.in. parametry metod produkujących (ang. producer methods), inicjujących (ang. initializer methods), rozgłaszających (ang. disposer methods), obserwujących (ang. observer methods) i konstruktorów.

Przestrzenie aktywności


Ziarna CDI istnieją w dobrze określonej przestrzeni, której cykl rozwojowy jest kontrolowany przez kontener. Wszystkie mają przestrzeń aktywności/zasięg działania (ang. scope).

Przestrzeń określona jest przez adnotację, np. @javax.enterprise.context.SessionScoped.

Istnieje 5 standardowych przestrzeni (wszystkie zdefiniowane w javax.enterprise.context):
  • @RequestScoped
  • @SessionScoped
  • @ApplicationScoped
  • @ConversationScoped
  • @Dependent
3 pierwsze reprezentują standardowe przestrzenie ze specyfikacji Java Servlets. Kolejny, @ConversationScoped to nowość w CDI, a ostatni podobnie, ale jest jeszcze dodatkowo specjalnego traktowania.

Można definiować własne przestrzenie. Tworzymy adnotację z @Target({TYPE, METHOD, FIELD}) i @Retention(RUNTIME). Dodatkowo obowiązkowo należy dodać @javax.inject.Scope lub @javax.enterprise.context.NormalScope.

Określenie przestrzeni ziarna to przypisanie adnotacji do jego typu lub metody/pola produkującego.

Brak jawnie określonej przestrzeni ziarna oznacza, że jest ona domyślna i zależy od stereotypów - ich brak to zasięg @Dependent, wspólna przestrzeń stereotypów to przestrzeń ziarna, a w przeciwnym przypadku musi być jawnie podana przestrzeń.

Jawne określenie przestrzeni, to przestrzeń obowiązująca (bez względu na przestrzenie deklarowane przez stereotypy).

Reszta rozdziału w kolejnej relacji, bo zaczyna się robić przydługo (i pewnie nudno).

Gdyby tylko jeszcze znaleźć ciekawe tłumaczenie dla angielskiego bean. Używam ziarno, ale coraz bardziej nie pasuje mi, jak i jego angielski protoplasta. Pomysły?

7 komentarzy:

  1. Mogę o tym czytać po angielsku, po polsku ni w cholerę nie rozumiem, język bardziej niż obcy.

    OdpowiedzUsuń
  2. Ćwicz umysł. Podobnie jak z nowymi językami programowania, szkieletami aplikacyjnymi, itp. Nowe pozwala spojrzeć na problem z innej perspektywy (w tym przypadku mojej i początkujących).

    OdpowiedzUsuń
  3. Zdecydowanie czasem przydałby sie jakiś choć trywialnie prosty przykład. Momentami się trzeba trochę zastanawiać jak to mogłoby wyglądać. No ale duża wiedza w jednej pigułce - Dzięki!

    Co do "ziaren" .. ja w swojej pracy mgr używać słowa bean i odpowiednio tego słowa odmian - beana, beanów, beanami, beanowi itp. W naszym świecie to już chyba tak utarte słowo jak choćby "web" (i piszemy nawet aplikacje webowe).

    OdpowiedzUsuń
  4. Tak, przykłady będą, ale "beana" pewnie nieprędko.

    OdpowiedzUsuń
  5. A masz może już jakieś przykłady kwalifikatorów? Nie czytałem specyfikacji (przyznaję się) i z Twojego opisu nie do końca potrafię sobie to wyobrazić w praktyce.

    Skoro z pomocą kwalifikatorów określam CO (w ziarnie) i GDZIE (w miejscu wstrzeliwania) może być wstrzelone, to jaka z tego korzyść, że muszę to i tak z obu stron to określać? Chyba, że mamy dostęp, do czegoś co jest dostępne tylko w runtime (np. jakieś warunkowe wstrzelenie w zależności od wartości).

    OdpowiedzUsuń
  6. Ok, odpowiedz na moje pytanie jest prosta. Opisane jest to w tym artykule: http://java.dzone.com/articles/cdi-overview-part-1

    Highlander rule :)

    OdpowiedzUsuń