Guru – Framework-Integration

Der CDI Decorator DebtLimitVerification bekommt so den DebtLimitService, der durch das Spring Framework verwaltet wird, über den CDI Container mittels @Inject injiziert.

Eine weitere interessante Abstraktion ist das InjectionTarget: Es ermöglicht die programmatische Injektion von Abhängigkeiten und bietet Methoden zur Steuerung des Lebenszyklus an. Im folgenden Beispiel wird die Klasse MoneyTransferController programmatisch über InjectionTarget erzeugt und alle Abhängigkeiten injiziert.

public class MoneyTransferController {

    MoneyTransferService moneyTransferService;
    
    @Inject Logger logger;

    @Inject
    public MoneyTransferController(MoneyTransferService moneyTransferService) {
        this.moneyTransferService = moneyTransferService;
    }

Zuerst wird der AnnotatedType und dann das InjectionTarget erzeugt:

AnnotatedType at =
                beanManager.createAnnotatedType(MoneyTransferController.class);
InjectionTarget it = 
                beanManager.createInjectionTarget(at);

Zur Erzeugung des MoneyTransferController wird ein CreationalContext benötigt, der aber für dieses Beispiel nicht weiter interessant ist.

CreationalContext ctx = 	
                beanManager.createCreationalContext(null);

Hier wird der MoneyTransferController über das InjectionTarget erzeugt und der MoneyTransferService über den Konstruktor injiziert. Wenn der MoneyTransferController nicht durch CDI erzeugt werden würden, wäre es natürlich auch nicht möglich, entsprechende Abhängigkeiten über den Konstruktor zu injizieren.

MoneyTransferController controller = it.produce(ctx);

Mittels der Methode inject werden die restlichen Abhängigkeiten an Feldern oder Methoden injiziert, in diesem Fall der Logger.

it.inject(controller, ctx);

Tabelle 1: Übersicht über die Events, die während des Initialisierungsprozesses des CDI Container verarbeitet werden können

Event Beschreibung

BeforeBeanDiscovery

Wird aufgerufen, bevor der Container alle Beans scannt. Dieses Event kann verwendet werden, um eigene Annotationen, Stereotypen, Qualifier etc. im Container zu registrieren.

ProcessAnnotatedType

Wird aufgerufen, nachdem eine Bean registriert wurde und bevor die Metadaten ausgewertet werden. Dieses Event kann verwendet werden, um Metadaten an Beans zu manipulieren.

ProcessInjectionTarget

Wird aufgerufen, bevor eine Bean Abhängigkeiten injiziert bekommt. Dieses Event kann verwendet werden, um das InjectionTarget zu dekorieren oder dies zu unterbinden.

ProcessProducer

Wird für jeden Producer aufgerufen. Dieses Event kann verwendet werden, um den Producer zu dekorieren oder die Registrierung des Producers zu unterbinden.

ProcessBean

Wird für jede Bean aufgerufen, bevor diese final im CDI Container registriert wird.

ProcessObserverMethod

Wird für jede Observer-Methode aufgerufen, bevor diese im CDI Container final registriert wird.

AfterBeanDiscovery

Wird aufgerufen, nach dem alle Beans gescannt wurden. Dieses Event kann genutzt werden, um eigene Beans, Interceptors, Decorators etc. zu registrieren.

AfterDeploymentValidation

Wird aufgerufen, wenn alle Beans registriert sind und deployed werden können.

Scopes

CDI bietet ein erweiterbares Context-Modell, mit dem neue Scope-Annotationen erstellt werden können [2]. Zusätzlich zu der Scope-Annotation muss ein Context-Objekt den Scope implementieren. Dies kann für Applikationen sehr nützlich sein, bei denen die DefaultScopes nicht ausreichen. Dadurch ist es dem Entwickler möglich, eine eigene Unit of Work zu definieren, in der ein Set von Objekten lebt. Sobald der Scope beendet wird, werden alle dazugehörigen Objekte durch den CDI Container automatisch aufgeräumt.

Im folgenden Beispiel aus Listing 3.1 wird ein Scope @CustomScoped erstellt, an dem die Implementierung demonstriert werden soll. Als Nächstes muss das javax.enterprise.context.spi.Context-Interface implementiert werden (Listing 3.2). Das Interface gehört zur SPI von CDI und enthält vier Methoden:

  • getScope()– liefert die Scope-Annotation des Context.
  • isActive()– beschreibt, ob der Context existiert.
  • get(Contextual) – findet die Instanz der Bean zu dem Context oder gibt null zurück.
  • get(Contextual, CreationalContext) – funktioniert wie get(Contextual) mit dem Unterschied, dass es eine Bean generiert, sollte keine gefunden werden.
Listing 3.1: Neuer ScopeType
@ScopeType
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface CustomScoped {}
Listing 3.2: Implementierung des Context für den neuen ScopeType
public class CustomContext implements Context {

   public Class getScope() {
       return CustomScoped.class;
   }

   public T get(Contextual contextual, CreationalContext creationalContext) {
     ...
   }

   public T get(Contextual contextual) {
	...
   }

   public boolean isActive() {
       return true;
   }
}

Damit die neu definierte Annotation aktiviert wird, muss das CustomContext-Objekt am Container registriert werden. Dazu wird eine Observer-Methode implementiert, die auf das AfterBeanDiscovery-Event reagiert.

public class CustomContextExtension implements Extension {

   public void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager manager) {
       event.addContext(new CustomContext());
   }
}

Für die meisten Applikationen sollten die DefaultScopes ausreichen. Zu den bekannten Scopes (RequestScoped, SessionScoped und ApplicationScoped) bietet CDI einen ConversationScope, wie es ihn auch im Seam Framework gibt. Dieser wird im folgenden Abschnitt betrachtet.

Der ConversationScope behält wie der SessionScope den Zustand für einen User über mehrere Requests hinweg. In JSF-Applikationen ist der ConversationScope mit genau einem Tab assoziiert und kapselt den Zustand dieses Tabs. Conversations werden direkt nach einem Request zerstört. Sollte dies nicht gewünscht sein, muss die Conversation explizit als long-running markiert werden. Hierfür stellt CDI eine Conversation Bean zur Verfügung, die den Lebenszyklus von Conversations in JSF-Applikationen kontrolliert. Diese Bean kann bei Bedarf in die Anwendung injiziert werden. Um ein Conversation als long-running zu markieren, muss die begin()-Methode aufgerufen werden. Wenn die Conversation abgeschlossen ist, kann die end()-Methode aufgerufen werden. Danach räumt der CDI Container alle Beans, die zu der Conversation gehören, automatisch auf und gibt die gebundenen Ressourcen wieder frei.

@ConversationScoped @Stateful
public class PaymentProcess {

  private @Inject Conversation conversation;
  private Payment payment;

  public Payment createPayment() {
    payment = new Payment();
    conversation.begin();
    return payment;
  }
  ...
  public void savePayment(Payment payment) {
    em.persist( payment );
    conversation.end();
  }
}

Wenn eine Conversation nach einer gewissen Zeit, in der sie nicht verwendet wurde, aufgeräumt werden soll, kann programmatisch ein Time-out definiert werden. Der CDI Container entscheidet anhand des Time-outs, wann er alle Ressourcen im Context der Conversation aufräumt. Folgender Code kann an der injizierten Conversation gesetzt werden:

conversation.setTimeout( timeoutInMilliseconds );
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.