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: