sobota, 4 grudnia 2010

Contexts and Dependency Injection – część I

Jako wielki entuzjasta Jboss Seam'a i korzystania z adnotacji, nie mogłem odpuścić sobie nauki specyfikacji CDI, (JSR-229), Jest to zbiór usług stanowiący integralną część standardu Java EE 6 i ułatwiający  powiązanie ze sobą poszczególnych komponentów (warstw) składających się  na powyższą platformę.

Contexts – przywiązanie komponentu Java EE do ściśle zdefiniowanego (ale rozszerzalnego) kontekstu (cyklu życia).
Dependency Injection – możliwość interakcji pomiędzy komponentami  aplikacji takimi jak serwlet, sesyjny EJB czy  JSF'owy managed bean poprzez wstrzyknięcie (Injection) czyli umieszczenie ich w dowolnym momencie w cyklu życia aplikacji.

Najlepiej naukę zacząć od praktycznego przykłady. Ja wykorzystam w tym celu środowisko NetBeans 6.9.1 z dostarczonym przez nie serwerem GlassFish 3.0.1. Zakładamy nowy nowy projekt: File › New Project › Web Application. Przy wyborze serwera należy pamiętać aby zaznaczyć opcję „Contexts and Dependency Injection”.
Powyższy krok, spowoduje nasza aplikacja będzie korzystać z implementacji CDI – WELD. W katalogu WEB-INF zostanie utworzony plik beans.xml, Jego istnienie sygnalizuje kontenerowi CDI, że aplikacja zawiera wstrzykiwane bean'y i musi być skanowana w poszukiwaniu klas zawierających adnotacje.

W kolejnym kroku z dostępnych technologii wybieram JSF

Celem aplikacji jest wstrzyknięcie do managed bean'a interfejsu odpowiedzialnego za logowanie.
public interface MyLogger {

    public void log(Class c, String message);
}

Implementacja wygląda następująco:
import javaspotlight.beans.MyLogger;
import javaspotlight.qualifiers.InfoLog;

public class InfoLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).info(message);
    }

Do wstrzyknięcia komponentu używamy adnotacji: @javax.inject.Inject
import java.util.logging.*;
import javaspotlight.beans.MyLogger;
import javaspotlight.qualifiers.InfoLog;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

@Named
@RequestScoped
public class ExampleManagedBean {

    private String outputMessage;

    @Inject
    MyLogger myLogger;

    public String exampleAction(){
        
        outputMessage = "Hello";

        myLogger.log(getClass(), outputMessage);
        return null;
    }
    


    public String getOutputMessage() {
        return outputMessage;
    }

    public void setOutputMessage(String outputMessage) {
        this.outputMessage = outputMessage;
    }

W sumie trywialne. Co jednak się stanie jeśli nasz interfejs będzie posiadał więcej implementacji ? Kontener nie będzie wiedział, której z nich należy użyć. Problem ten rozwiązują kwalifikatory (Qualifiers). Pozwalają nam one na przypisanie dodatkowych informacji do naszego bean'a.

Ja utworzyłem dwie implementacjie loggera:
@InfoLog
public class InfoLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).info(message);
    }
}

@WarnLog
public class WarnLogger implements MyLogger{

    @Override
    public void log(Class c, String message) {

      Logger.getLogger(c.getName()).warning(message);
    }
}

Teraz aby móc wskazywać przy pomocy adnotacji na konkretny typ, należy utworzyć kwalifikatory
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface InfoLog {
}

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface WarnLog {
}

dzięki nim, wstrzykując nasz interfejs łatwo możemy wskazać jakiego typu bean zostać użyty.
@Inject
@WarnLog
MyLogger myLogger;

albo
@Inject
@InfoLog
MyLogger myLogger;

Struktura mojego projektu wygląda następująco: