CDI - Context and Dependency Injection

CDI (Context and Dependency Injection) jest standardem wstrzykiwania zależności w Javie EE. Możliwe jest wykorzystanie implementacji również w Javie SE. Aktualna wersja oznaczona jest numerem 1.2, jednak w Javie EE 8 pojawi się wersja CDI 2.0. Specyfikacja definiująca CDI oznaczona jest jako JSR 299.

Implementacje

CDI podobnie jak wiele składowych Javy EE jest jedynie specyfikacją do tego jakie wymogi mają spełniać jej implementacje. Najpopularniejszą i referencyjną implementacją jest Weld rozwijany pod skrzydłami firmy redhat. Wykorzystywany jest on zarówno w serwerach redhata jak JBoss i Wildfly, ale również w innych np. Glassfishu. Rzadziej spotykaną alternatywą jest Apache OpenWebBeans.

W przypadku Welda dostępna jest również wersja Weld-SE, która pozwala na wykorzystanie wstrzykiwania zależności w tradycyjnych aplikacjach desktopowych, które nie będą uruchamiane w kontenerach Javy EE.

Możliwości

Specyfikacja CDI łączy w sobie nie tylko możliwości wstrzykiwania zależności, ale definiuje również standard programowania aspektowego, który realizowany jest w Javie EE poprzez mechanizm interceptorów.

CDI świetnie integruje się z większością już istniejących specyfikacji dzięki czemu możemy z niego korzystać w technologiach takich jak serwlety, EJB, czy JaxRS.

Konfiguracja

W przypadku, gdy korzystamy z Mavena i aplikacji, która będzie uruchamiana w kontenerze Javy EE, powinniśmy posługiwać się zależnościami dostarczanymi przez kontener, z którego korzystamy. Jeśli jest to serwer zgodny z Javą EE 7 to korzystając z Mavena wystarczy dodać zależność:

<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>1.2</version>
</dependency>

Najlepszym pomysłem jest korzystanie z sekcji dependencyManagement , dzięki czemu możliwe będzie pominięcie definiowania wersji i ustawienie zasięgu na provided.

Chcąc skorzystać z CDI w Javie SE w projekcie o naturze mavena należy dodać zależność do biblioteki Weld SE:

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-core</artifactId>
    <version>2.3.4.Final</version>
</dependency>

lub pobrać bibliotekę w formie .jar z oficjalnej strony. Zwracamy uwagę, żeby pobrać wersję ze wszystkimi zależnościami (Uber Jar).

Wstrzykiwanie zależności (Weld)

Wstrzykiwanie zależności jest techniką, która pozwala nam unikać silnych powiązań pomiędzy klasami. Silną rolę odgrywa tutaj korzystanie z interfejsów oraz zalet jakie daje nam polimorfizm.

Konfiguracja projektu

Pobierz najnowszą wersję biblioteki Weld-SE i dodaj ją do swojego projektu. W przypadku Eclipse najlepiej utworzyć w tym celu folder lib, w którym należy umieścić bibliotekę, a następnie kliknij na nią prawym przyciskiem myszy i wybierz opcję Build Path . Add to build path.

cdi project eclipse

W przypadku projektu Mavena dodaj jedną z wcześniej podawanych zależności.

Drugim krokiem jest kliknięcie prawym przyciskiem myszy na projekcie i przejście do konfiguracji (Properties), następnie wskazujemy Project Facets > Convert to faceted form . Dzięki temu będziemy mogli zaznaczyć, że w projekcie chcemy wsparcie dla CDI i wygenerowany zostanie jedyny wymagany plik konfiguracyjny o nazwie beans.xml.

project facet konfiguracja

W przypadku aplikacji Java SE plik beans.xml zostanie utworzony w folderze META-INF, a w aplikacjach webowych w WEB-INF.

Definiowanie ziaren

W przypadku frameworków udostępniających nam kontener wstrzykiwania zależności takich jak CDI, czy Spring, obiekty zarządzane przez kontener nazywać będziemy ziarnami. W przypadku Springa do dyspozycji mamy konfigurację opartą o XML i w nowszych wersjach o adnotacje Javy. Ponieważ CDI powstało później i wynika z najlepszych praktyk, to zdecydowano się konfigurację XML ograniczyć do minimum, stawiając na podejście convention over configuration i stosowanie adnotacji.

W celu wskazania klasy jako ziarno zarządzane CDI należy je opatrzyć jedną z adnotacji zasięgu np. @Dependent , czy @RequestScoped.

cdi eclipse project 2

Najpierw definiujemy klasę HelloService, której zadaniem jest zwrócenie komunikatu powitalnego.

package pl.javastart.bean;

import javax.enterprise.context.Dependent;

@Dependent
public class HelloService {

    public String getMessage() {
        return "Hello world from Hello Service!";
    }
}

Następnie komunikat ten chcemy wykorzystać w klasie SayHelloService, więc musimy do niego wstrzyknąć instancję powyższej klasy, co robimy poprzez adnotację @Inject, która wykorzystywana jest do wstrzykiwania większości rzeczy, również ziaren EJB.

package pl.javastart.bean;

import javax.enterprise.context.Dependent;
import javax.inject.Inject;

@Dependent
public class SayHelloService {

    @Inject
    private HelloService helloService;
    
    public void sayHello() {
        System.out.println(helloService.getMessage());
    }
}

Uruchamianie kontenera

Instancję SayHelloService chcemy teraz pobrać z kontenera Welda i wywołać na niej metodę sayHello().

package pl.javastart.app;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;

import pl.javastart.bean.SayHelloService;

public class CdiTest {
    public static void main(String[] args) {
        Weld weld = new Weld();
        WeldContainer container = weld.initialize();
        SayHelloService sayHelloService = container.select(SayHelloService.class).get();
        sayHelloService.sayHello();
    }
}

Po uruchomieniu aplikacji w konsoli pojawią się logi Welda, a następnie komunikat wydrukowany przez metodę sayHello(). Jak widać korzystając z kontenera wstrzykiwania zależności odpowiedzialność za tworzenie obiektów i zarządzanie ich cyklem życia stoi po stronie kontenera, a nie naszej - nigdzie nie wywołujemy konstruktorów, ani nie ustawiamy zależności poprzez settery.

W przypadku aplikacji tworzonych w środowisku Java EE kontener DI pracuje w tle, nie będzie więc potrzebne definiowania obiektów Weld, czy WeldContainer.

console log weld

Dyskusja i komentarze

Masz pytania do tego wpisu? Może chcesz się podzielić spostrzeżeniami? Zapraszamy dyskusji na naszej grupie na Facebooku.