Einführung in das Ereignisbenachrichtigungsmodell in CDI 2.0

1. Überblick

CDI (Contexts and Dependency Injection) ist das Standard-Framework zur Abhängigkeitsinjektion der EE-Plattform in Jakarta.

In diesem Tutorial werfen wir einen Blick auf http://www.cdi-spec.org/news/2017/05/15/CDI 2 is__released/[CDI 2.0]und erfahren, wie es auf dem leistungsstarken, typsicheren Einspritzmechanismus aufbaut von CDI 1.x durch Hinzufügen eines verbesserten Ereignismeldungsmodells mit allen Funktionen.

2. Die Maven-Abhängigkeiten

Zu Beginn erstellen wir ein einfaches Maven-Projekt.

  • Wir brauchen einen CDI 2.0-kompatiblen Container, und Weld , die Referenzimplementierung von CDI, passt gut:

<dependencies>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>2.0.SP1</version>
    </dependency>
    <dependency>
        <groupId>org.jboss.weld.se</groupId>
        <artifactId>weld-se-core</artifactId>
        <version>3.0.5.Final</version>
    </dependency>
</dependencies>

Wie üblich können wir die neuesten Versionen von cdi-api und https://suchen .maven.org/search? q = g: org.jboss.weld.se% 20AND% 20a: Schweißnahtkern & Kern = gav[Schweißnahtkern] von Maven Central.

3. Beobachten und Verwalten von benutzerdefinierten Ereignissen

Einfach ausgedrückt: Das CDI 2.0-Ereignisbenachrichtigungsmodell ist eine klassische Implementierung des Observer-Musters , basierend auf @ Observes Annotation der Methodenparameter. Somit können Beobachtermethoden definiert werden, die automatisch als Reaktion auf ein oder mehrere Ereignisse aufgerufen werden können.

Beispielsweise könnten wir eine oder mehrere Beans definieren, die ein oder mehrere bestimmte Ereignisse auslösen würden, während andere Beans über die Ereignisse benachrichtigt würden und entsprechend reagieren würden.

Um dies besser zu veranschaulichen, erstellen wir ein einfaches Beispiel, das eine grundlegende Serviceklasse, eine benutzerdefinierte Ereignisklasse und eine Observer-Methode umfasst, die auf unsere benutzerdefinierten Ereignisse reagiert.

** 3.1. Eine Basisdienstklasse

**

Beginnen wir mit dem Erstellen einer einfachen TextService -Klasse:

public class TextService {

    public String parseText(String text) {
        return text.toUpperCase();
    }
}

3.2. Eine benutzerdefinierte Ereignisklasse

Als Nächstes definieren wir eine Beispielereignisklasse, die im Konstruktor ein String -Argument enthält:

public class ExampleEvent {

    private final String eventMessage;

    public ExampleEvent(String eventMessage) {
        this.eventMessage = eventMessage;
    }

   //getter
}

3.3. Definieren einer Observer-Methode mit der Annotation @ Observes

Nachdem wir nun unsere Service- und Event-Klassen definiert haben, verwenden wir die Annotation @ Observes , um eine Observer-Methode für unsere ExampleEvent -Klasse zu erstellen:

public class ExampleEventObserver {

    public String onEvent(@Observes ExampleEvent event, TextService textService) {
        return textService.parseText(event.getEventMessage());
    }
}

Auf den ersten Blick wirkt die Implementierung der onEvent () - Methode ziemlich trivial, sie enthält jedoch eine Vielzahl von Funktionen durch die Annotation @ Observes .

Wie wir sehen können, ist die onEvent () - Methode ein Event-Handler, der ExampleEvent - und TextService -Objekte als Argumente verwendet.

  • Beachten Sie, dass alle nach der Annotation @ Observes angegebenen Argumente Standardinjektionspunkte sind. ** Als Ergebnis erstellt CDI vollständig initialisierte Instanzen für uns und injiziert sie in die Observer-Methode.

3.4. Initialisierung unseres CDI 2.0-Containers

Zu diesem Zeitpunkt haben wir unsere Service- und Event-Klassen erstellt und eine einfache Beobachtermethode definiert, um auf unsere Ereignisse zu reagieren. Wie aber weisen wir CDI an, diese Instanzen zur Laufzeit einzuspeisen?

Hier zeigt das Ereignisbenachrichtigungsmodell seine Funktionalität voll aus. Wir initialisieren einfach die neue Implementierung von SeContainer und lösen ein oder mehrere Ereignisse über die fireEvent () -Methode aus:

SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance();
try (SeContainer container = containerInitializer.initialize()) {
    container.getBeanManager().fireEvent(new ExampleEvent("Welcome to Baeldung!"));
}
  • Beachten Sie, dass wir die SeContainerInitializer - und SeContainer -Objekte verwenden, da wir CDI in einer Java SE-Umgebung und nicht in Jakarta EE verwenden. **

Alle angehängten Observer-Methoden werden benachrichtigt, wenn ExampleEvent durch Auslösen des Ereignisses ausgelöst wird.

Da alle nach der Annotation @ Observes als Argumente übergebenen Objekte vollständig initialisiert werden, kümmert sich CDI darum, den gesamten TextService -Objektgraph für uns zu verkabeln, bevor er in die onEvent () -Methode eingefügt wird.

Kurz gesagt: Wir haben die Vorteile eines typsicheren IoC-Containers zusammen mit einem Ereignisbenachrichtigungsmodell mit vielen Funktionen .

4. Das ContainerInitialized -Ereignis

Im vorherigen Beispiel haben wir ein benutzerdefiniertes Ereignis verwendet, um ein Ereignis an eine Observer-Methode zu übergeben und ein vollständig initialisiertes TextService -Objekt zu erhalten.

Dies ist natürlich nützlich, wenn wir wirklich ein oder mehrere Ereignisse über mehrere Punkte unserer Anwendung verbreiten müssen.

  • Manchmal benötigen wir einfach eine Reihe von vollständig initialisierten Objekten, die innerhalb unserer Anwendungsklassen verwendet werden können ** , ohne dass zusätzliche Ereignisse implementiert werden müssen.

Zu diesem Zweck bietet CDI 2.0 die http://javadox.com/org.jboss.weld.se/weld-se-core/2.2.6.Final/org/jboss/weld/environment/se/events/ContainerInitialized .html[ ContainerInitialized ]-Ereignisklasse, die automatisch ausgelöst wird, wenn der Weld-Container initialisiert wird.

Schauen wir uns an, wie wir das ContainerInitialized -Ereignis zum Übertragen des Steuerelements an die ExampleEventObserver -Klasse verwenden können:

public class ExampleEventObserver {
    public String onEvent(@Observes ContainerInitialized event, TextService textService) {
        return textService.parseText(event.getEventMessage());
    }
}

Beachten Sie, dass die ContainerInitialized -Ereignisklasse Weld-spezifisch ist. Wenn wir eine andere CDI-Implementierung verwenden, müssen wir unsere Observer-Methoden umgestalten.

5. Bedingte Beobachtungsmethoden

In der aktuellen Implementierung definiert unsere Klasse ExampleEventObserver standardmäßig eine bedingungslose Observer-Methode. Dies bedeutet, dass die Observer-Methode immer über das bereitgestellte Ereignis informiert wird , unabhängig davon, ob eine Instanz der Klasse im aktuellen Kontext vorhanden ist.

Ebenso können Sie eine bedingte Observer-Methode definieren, indem Sie notifyObserver = IF EXISTS als Argument für die Annotation @ Observes__ angeben:

public String onEvent(@Observes(notifyObserver=IF__EXISTS) ExampleEvent event, TextService textService) {
    return textService.parseText(event.getEventMessage());
}

Wenn wir eine bedingte Observer-Methode verwenden, wird die Methode über das übereinstimmende Ereignis nur dann benachrichtigt, wenn eine Instanz der Klasse, die die Observer-Methode definiert, im aktuellen Kontext vorhanden ist.

6. Transaktionsbeobachtermethoden

  • Wir können auch Ereignisse innerhalb einer Transaktion auslösen, z. B. eine Datenbankaktualisierung oder ein Entfernen. ** Zu diesem Zweck können wir Transaktionsbeobachtermethoden definieren, indem Sie das during -Argument zur @ Observes -Annotation hinzufügen.

Jeder mögliche Wert des during -Arguments entspricht einer bestimmten Phase einer Transaktion:

  • BEFORE COMPLETION__

  • NACH FERTIGSTELLUNG

  • AFTER SUCCESS__

  • AFTER FAILURE__

Wenn wir das ExampleEvent -Ereignis innerhalb einer Transaktion auslösen, müssen wir die onEvent () -Methode entsprechend umgestalten, um das Ereignis in der erforderlichen Phase zu behandeln:

public String onEvent(@Observes(during=AFTER__COMPLETION) ExampleEvent event, TextService textService) {
    return textService.parseText(event.getEventMessage());
}
  • Eine Transaktionsbeobachtungsmethode wird nur in der Matching-Phase einer bestimmten Transaktion über das bereitgestellte Ereignis informiert. **

7. Observer-Methodenbestellung

Eine weitere schöne Verbesserung des Ereignisbenachrichtigungsmodells von CDI 2.0 ist die Möglichkeit, eine Reihenfolge oder Priorität für das Aufrufen von Beobachtern eines bestimmten Ereignisses festzulegen.

Die Reihenfolge, in der die Observer-Methoden aufgerufen werden, kann leicht definiert werden, indem die Annotation @Priority nach @ Observes angegeben wird .

Um zu verstehen, wie diese Funktion funktioniert, definieren wir neben der von ExampleEventObserver implementierten eine andere Observer-Methode:

public class AnotherExampleEventObserver {

    public String onEvent(@Observes ExampleEvent event) {
        return event.getEventMessage();
    }
}
  • In diesem Fall haben beide Observer-Methoden standardmäßig die gleiche Priorität. Daher ist die Reihenfolge, in der CDI sie aufrufen wird, einfach unvorhersehbar ** .

Wir können dies leicht beheben, indem Sie jeder Methode eine Aufrufpriorität durch die Annotation @ Priority zuweisen:

public String onEvent(@Observes @Priority(1) ExampleEvent event, TextService textService) {
   //... implementation
}
public String onEvent(@Observes @Priority(2) ExampleEvent event) {
   //... implementation
}
  • Prioritätsstufen folgen einer natürlichen Reihenfolge. ** Daher ruft CDI zuerst die Observer-Methode mit einer Prioritätsstufe von 1 auf und zweitens die Methode mit einer Prioritätsstufe von 2 .

Ebenso ist die Reihenfolge, wenn wir dieselbe Prioritätsstufe für zwei oder mehr Methoden verwenden, undefiniert .

8. Asynchrone Ereignisse

In allen Beispielen, die wir bisher gelernt haben, haben wir Ereignisse synchron ausgelöst. Mit CDI 2.0 können jedoch auch asynchrone Ereignisse problemlos ausgelöst werden. Asynchrone Observer-Methoden können dann diese asynchronen Ereignisse in verschiedenen Threads behandeln.

Wir können ein Ereignis asynchron mit der Methode fireAsync () auslösen:

public class ExampleEventSource {

    @Inject
    Event<ExampleEvent> exampleEvent;

    public void fireEvent() {
        exampleEvent.fireAsync(new ExampleEvent("Welcome to Baeldung!"));
    }
}
  • Beans starten Ereignisse, bei denen es sich um Implementierungen der Event -Schnittstelle handelt. Daher können wir sie wie jede andere herkömmliche Bohne ** injizieren.

Um mit unserem asynchronen Ereignis umgehen zu können, müssen Sie eine oder mehrere asynchrone Observer-Methoden mit @ObservesAsync definieren. Anmerkung:

public class AsynchronousExampleEventObserver {

    public void onEvent(@ObservesAsync ExampleEvent event) {
       //... implementation
    }
}

9. Fazit

In diesem Artikel haben wir erfahren, wie Sie mit dem verbesserten Ereignisbenachrichtigungsmodell, das mit CDI 2.0 geliefert wird, beginnen.

Alle in diesem Lernprogramm gezeigten Codebeispiele sind wie üblich unter GitHub verfügbar.