Frühlingsereignisse

Frühlingsereignisse

1. Überblick

In diesem Artikel werden wirhow to use events in Spring diskutieren.

Ereignisse sind eine der am häufigsten übersehenen Funktionen im Framework, aber auch eine der nützlicheren. Und - wie viele andere Dinge im Frühjahr - ist das Veröffentlichen von Ereignissen eine der Funktionen, dieApplicationContext. bietet

Es gibt ein paar einfache Richtlinien zu beachten:

  • Das Ereignis sollteApplicationEvent verlängern

  • Der Herausgeber sollte einApplicationEventPublisher-Objekt einfügen

  • Der Listener sollte dieApplicationListener-Schnittstelle implementieren

2. Ein benutzerdefiniertes Ereignis

Mit Spring können benutzerdefinierte Ereignisse erstellt und veröffentlicht werden, die standardmäßigare synchronous betragen. Dies hat einige Vorteile - wie zum Beispiel, dass der Listener am Transaktionskontext des Publishers teilnehmen kann.

2.1. Ein einfaches Anwendungsereignis

Erstellen wira simple event class - nur einen Platzhalter zum Speichern der Ereignisdaten. In diesem Fall enthält die Ereignisklasse eine String-Nachricht:

public class CustomSpringEvent extends ApplicationEvent {
    private String message;

    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}

2.2. Ein Verlag

Jetzt erstellen wira publisher of that event. Der Herausgeber erstellt das Ereignisobjekt und veröffentlicht es für alle, die zuhören.

Um das Ereignis zu veröffentlichen, kann der Herausgeber einfach dieApplicationEventPublisher einfügen und diepublishEvent()-API verwenden:

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

Alternativ kann die Publisher-Klasse dieApplicationEventPublisherAware-Schnittstelle implementieren. Dadurch wird auch der Event-Publisher beim Start der Anwendung eingebunden. Normalerweise ist es einfacher, dem Herausgeber nur@Autowire. zu injizieren

2.3. Ein Zuhörer

Zum Schluss erstellen wir den Listener.

Die einzige Voraussetzung für den Listener ist, eine Bean zu sein und die Schnittstelle vonApplicationListenerzu implementieren:

@Component
public class CustomSpringEventListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

Beachten Sie, wie unser benutzerdefinierter Listener mit dem generischen Typ des benutzerdefinierten Ereignisses parametrisiert wird, wodurch der MethodentyponApplicationEvent()icher wird. Dadurch wird auch vermieden, dass überprüft werden muss, ob das Objekt eine Instanz einer bestimmten Ereignisklasse ist, und diese umgewandelt wird.

Und wie bereits erläutert - standardmäßigspring events are synchronous - blockiert die MethodedoStuffAndPublishAnEvent(), bis alle Listener die Verarbeitung des Ereignisses abgeschlossen haben.

3. Asynchrone Ereignisse erstellen

In einigen Fällen ist das synchrone Veröffentlichen von Ereignissen nicht das, wonach wir suchen -we may need async handling of our events.

Sie können dies in der Konfiguration aktivieren, indem Sie mit einem Executor eineApplicationEventMulticaster-Bean erstellen. Für unsere Zwecke hier funktioniertSimpleAsyncTaskExecutor gut:

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster =
          new SimpleApplicationEventMulticaster();

        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

Die Implementierungen für Ereignis, Herausgeber und Listener bleiben unverändert - jetzt jedochthe listener will asynchronously deal with the event in a separate thread.

4. Bestehende Framework-Ereignisse

Spring selbst veröffentlicht eine Vielzahl von Ereignissen, die sofort einsatzbereit sind. Beispielsweise lösen dieApplicationContext verschiedene Framework-Ereignisse aus. E.g. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent usw.

Diese Ereignisse bieten Anwendungsentwicklern die Möglichkeit, sich in den Lebenszyklus der Anwendung und den Kontext einzuklinken und bei Bedarf eine eigene benutzerdefinierte Logik hinzuzufügen.

Hier ist ein kurzes Beispiel für einen Listener, der auf Kontextaktualisierungen wartet:

public class ContextRefreshedListener
  implements ApplicationListener {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

Weitere Informationen zu vorhandenen Framework-Ereignissen finden Sie unterour next tutorial here.

5. Anmerkungsgesteuerter Ereignis-Listener

Ab Spring 4.2 muss ein Ereignis-Listener keine Bean sein, die dieApplicationListener-Schnittstelle implementiert. Er kann über die@EventListener-Annotation auf einer beliebigenpublic-Methode einer verwalteten Bean registriert werden:

@Component
public class AnnotationDrivenContextStartedListener {
    // @Async
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event.");
    }
}

Wie zuvor deklariert die Methodensignatur den Ereignistyp, den sie verbraucht. Dieser Listener wird wie bisher synchron aufgerufen. Das asynchrone Erstellen ist jetzt so einfach wie das Hinzufügen einer@Async-Annotation (vergessen Sie nicht,enable Async support in der Anwendung zu verwenden).

6. Generika-Support

Es ist auch möglich, Ereignisse mit allgemeinen Informationen im Ereignistyp zu versenden.

6.1. Ein generisches Anwendungsereignis

Let’s create a generic event type. In unserem Beispiel enthält die Ereignisklasse einen beliebigen Inhalt und eine Statusanzeige vonsuccess:

public class GenericSpringEvent {
    private T what;
    protected boolean success;

    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}

Beachten Sie den Unterschied zwischenGenericSpringEvent undCustomSpringEvent. Wir haben jetzt die Flexibilität, beliebige Ereignisse zu veröffentlichen, und es ist nicht mehr erforderlich, vonApplicationEvent zu verlängern.

6.2. Ein Zuhörer

Jetzt erstellen wira listener of that event. Wir könnten den Listener definieren, indem wir die Schnittstelle vonthe ApplicationListenerwie zuvor implementieren:

@Component
public class GenericSpringEventListener
  implements ApplicationListener> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent event) {
        System.out.println("Received spring generic event - " + event.getWhat());
    }
}

Leider erfordert diese Definition, dass wirGenericSpringEvent von der KlasseApplicationEvent erben. Verwenden wir für dieses Tutorial einen annotationsgesteuerten Ereignis-Listener, der inpreviously beschrieben wird.

Es ist auch möglich,make the event listener conditional zu definieren, indem ein boolescher SpEL-Ausdruck in der Annotation@EventListenerdefiniert wird. In diesem Fall wird der Ereignishandler nur für erfolgreicheGenericSpringEvent vonString aufgerufen:

@Component
public class AnnotationDrivenEventListener {
    @EventListener(condition = "#event.success")
    public void handleSuccessful(GenericSpringEvent event) {
        System.out.println("Handling generic event (conditional).");
    }
}

Spring Expression Language (SpEL) ist eine leistungsstarke Ausdruckssprache, die in einem anderen Lernprogramm ausführlich behandelt wird.

6.3. Ein Verlag

Der Ereignisverleger ähnelt dem beschriebenenabove. Aufgrund der Typlöschung müssen wir jedoch ein Ereignis veröffentlichen, das den generischen Parameter auflöst, nach dem wir filtern würden. Zum Beispielclass GenericStringSpringEvent extends GenericSpringEvent<String>.

Und es gibtan alternative way of publishing events. Wenn wir einen Wert ungleich Null von einer Methode zurückgeben, die mit@EventListener als Ergebnis versehen ist, sendet Spring Framework dieses Ergebnis als neues Ereignis für uns. Darüber hinaus können wir mehrere neue Ereignisse veröffentlichen, indem wir sie als Ergebnis der Ereignisverarbeitung in einer Sammlung zurückgeben.

7. Transaktionsgebundene Ereignisse

In diesem Absatz wird die Annotation@TransactionalEventListenerverwendet. Weitere Informationen zum Transaktionsmanagement finden Sie im Tutorial vonTransactions with Spring and JPA.

Seit Spring 4.2 bietet das Framework eine neue Annotation@TransactionalEventListener, bei der es sich um eine Erweiterung von@EventListener handelt, mit der der Listener eines Ereignisses an eine Phase der Transaktion gebunden werden kann. Die Anbindung an folgende Transaktionsphasen ist möglich:

  • AFTER_COMMIT (Standard) wird verwendet, um das Ereignis auszulösen, wenn die Transaktioncompleted successfully enthält

  • AFTER_ROLLBACK - wenn die Transaktionrolled back hat

  • AFTER_COMPLETION - wenn die Transaktioncompleted hat (ein Alias ​​fürAFTER_COMMIT undAFTER_ROLLBACK)

  • BEFORE_COMMIT wird verwendet, um das Ereignisrechtbefore Transaktioncommit auszulösen

Hier ist ein kurzes Beispiel für den Listener von Transaktionsereignissen:

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}

Dieser Listener wird nur aufgerufen, wenn es eine Transaktion gibt, in der der Event-Produzent ausgeführt wird und die festgeschrieben werden soll.

Wenn keine Transaktion ausgeführt wird, wird das Ereignis überhaupt nicht gesendet, es sei denn, wir überschreiben dies, indem wir das AttributfallbackExecutionauftrue setzen.

8. Fazit

In diesem kurzen Tutorial gingen wir auf die Grundlagen vondealing with events in Spring ein - Erstellen eines einfachen benutzerdefinierten Ereignisses, Veröffentlichen und anschließendes Behandeln in einem Listener.

Wir haben uns auch kurz angesehen, wie die asynchrone Verarbeitung von Ereignissen in der Konfiguration aktiviert wird.

Anschließend wurden die in Spring 4.2 eingeführten Verbesserungen erläutert, z. B. durch Anmerkungen gesteuerte Listener, bessere Unterstützung für Generika und Ereignisse, die an Transaktionsphasen gebunden sind.

Wie immer ist der in diesem Artikel vorgestellte Codeover on Github verfügbar. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.