Sierpień
31
2009

Seam – wstrzykiwanie zależności

Słowa kluczowe: | Kategorie: Seam Framework
No Gravatar

Dzisiaj pokrótce omówię podstawy wstrzykiwania zależności. Przede mną sporo kodu, więc bez dłuższego wstępu przejdę do sedna problemu.

Moim zadaniem będzie przetestowanie działania listy elementów tworzonych różnymi metodami. Na początek przygotuję interfejs elementu:


public interface Item {
    public void test();
}

Item.java

Jedyna metoda (test()) posłuży mi do sprawdzenia, czy zawartość elementu jest poprawna.

Lista elementów będzie komponentem o nazwie listBean, ładowanym do kontekstu strony:

@Name("listBean")
@Scope(ScopeType.PAGE)
public class ListBean {
    private LogProvider log =
        Logging.getLogProvider(ListBean.class);

    private List<Item> items;

    @Create
    public void create() {
        log.info("Bean created.");
    }

    @Unwrap
    public List<Item> getItems() {
        if (items == null) {
            items = new ArrayList<Item>();
        }
        /* Tutaj dodawanie elementów listy. */
        log.info("Done.");
        return items;
    }
}

ListBean.java

Komponent zawiera następujące składowe:

log
obiekt zapewniający zapis informacji do dzienników serwera,
items
lista elementów,
create()
metoda wywoływana w momencie tworzenia komponentu – zapewni informację o utworzeniu komponentu,
getItems()
metoda pobierająca listę elementów – dzięki adnotacji @Unwrap pod nazwą komponentu listBean kontener będzie rozumiał obiekt zwracany przez tę metodę.

Sam element listy będzie podobnym komponentem, zawierającym wstrzyknięty manager encji:


@Name("itemBean")
@Scope(ScopeType.PAGE)
public class ItemBean implements Item {
    private LogProvider log =
        Logging.getLogProvider(ItemBean.class);

    @In
    private EntityManager entityManager;

    @Create
    public void create() {
        log.info("Bean created.");
    }

    public void test() {
        if (entityManager == null) {
            log.info("Entity manager is null.");
        } else {
            log.info("Entity manager exists.");
        }
    }
}

ItemBean.java

Dzięki metodzie test() będę wiedział, czy wstrzykiwanie zależności zadziałało.

Na sam koniec tworzę widok, który wymusi wywołanie metody getItems() klasy ListBean:

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    template="/layout/template.xhtml">
  <ui:define name="body">
    <h:outputText value="#{listBean.size()}"/>
  </ui:define>
</ui:composition>

Widok ten będzie wyświetlał liczbę odpowiadającą ilości elementów listy.

Wywołanie konstruktora

W miejscu linii 19 z ListBean.java będę dodawał do listy pojedynczy element. Zacznę od standardowego sposobu, czyli od wywołania konstruktora:

log.info("Creating item:");
items.add(new ItemBean());
items.get(0).test();

Po uruchomieniu strony test.seam, w dziennikach serwera pojawiają się następujące informacje:

20:28:00,657 INFO  [ListBean] Bean created.
20:28:00,658 INFO  [ListBean] Creating item:
20:28:00,658 INFO  [ItemBean] Entity manager is null.
20:28:00,658 INFO  [ListBean] Done.

Na początku tworzony jest komponent listBean, po czym dodawany jest nowy element listy. Pole ItemBean.entityManager jest puste, więc wstrzykiwanie zależności do tak utworzonego obiektu nie działa. Co więcej, metoda ItemBean.create() nie została w ogóle wywołana.

Dzieje się tak dlatego, że element listy został utworzony jako zwykły obiekt (POJO), a nie jako komponent. Oznacza to, że adnotacje Seam (@In, @Create) nie zadziałają.

Pobieranie instancji komponentu

Po skasowaniu trzech ostatnio dodanych linijek, spróbuję pobrać nową instancję komponentu przy pomocy metody Component.getInstance():

log.info("Creating item:");
items.add((Item)
    Component.getInstance("itemBean", true));
items.get(0).test();

Tym razem rezultat działania strony jest nieco inny:

20:29:15,292 INFO  [ListBean] Bean created.
20:29:15,293 INFO  [ListBean] Creating item:
20:29:15,308 INFO  [ItemBean] Bean created.
20:29:15,309 INFO  [ItemBean] Entity manager exists.
20:29:15,309 INFO  [ListBean] Done.

Seam pobrał instancję komponentu itemBean z kontekstu. Parametr true wskazuje, że w przypadku braku instancji, ma zostać utworzona nowa.

Wstrzykiwanie zależności

Na sam koniec pozostawiłem wstrzykiwanie zależności. Zacznę od dodania nowego pola do komponentu listBean:

@In(create = true)
private Item itemBean;

Bez żadnych dodatkowych operacji, dodam tak wstrzyknięty obiekt do listy (zamiast wywołania konstruktora lub Component.getInstance()):

log.info("Creating item:");
items.add(itemBean);
items.get(0).test();

Tym razem rezultat jest jeszcze inny:

20:30:57,126 INFO  [ItemBean] Bean created.
20:30:57,126 INFO  [ListBean] Bean created.
20:30:57,127 INFO  [ListBean] Creating item:
20:30:57,127 INFO  [ItemBean] Entity manager exists.
20:30:57,127 INFO  [ListBean] Done.

Ponieważ listBean wymaga do działania wstrzyknięcia komponentu itemBean, to element tworzony jest jeszcze przed listą.

Różnice

Wydaje się, że tworzenie obiektów za pomocą konstruktora jest niewskazane, bo wówczas tracą one wsparcie ze strony Seam Framework… nieprawda.

Komponent pobierany z kontekstu ma tę szczególną cechę, że za każdym razem jest to ta sama instancja. Czasem jest to zaleta, czasem wada, ale warto brać tę różnicę pod uwagę podczas projektowania aplikacji.

Inny dylemat pojawia się przy porównaniu dwóch ostatnich sytuacji. Stosowanie wstrzykiwania zależności wydaje się lepsze, prostrze i „ładniejsze”, ale nie zawsze jest dobre. Dodatkowe wywołanie metody Component.getInstance() może okazać się przydatne, a nawet niezbędne.

Gdy wstrzykiwany komponent wykorzystywany jest tylko w szczególnych sytuacjach, a jego tworzenie wymaga szeregu złożonych operacji, pobieranie instancji z pomocą klasy Component pomoże zoptymalizować szybkość aplikacji.

Zdarzają się sytuacje, gdy potrzeba utworzyć komponenty kilku różnych klas, implementujące jeden interfejs, wybierając je dynamicznie. Wówczas jedynym rozwiązaniem okazuje się pobieranie odpowiednich instancji w pętli (lepsze może okazać się wykorzystanie obserwatorów, ale to już temat na osobny artykuł).

Napisz Komentarz

*