Événements printaniers

Événements de printemps

1. Vue d'ensemble

Dans cet article, nous parlerons dehow to use events in Spring.

Les événements sont l’une des fonctionnalités les plus négligées du cadre, mais aussi l’une des plus utiles. Et - comme beaucoup d'autres choses dans Spring - la publication d'événements est l'une des fonctionnalités fournies parApplicationContext.

Il y a quelques directives simples à suivre:

  • l'événement devrait étendreApplicationEvent

  • l'éditeur doit injecter un objetApplicationEventPublisher

  • l'auditeur doit implémenter l'interfaceApplicationListener

2. Un événement personnalisé

Spring permet de créer et de publier des événements personnalisés qui - par défaut -are synchronous. Cela présente quelques avantages, tels que, par exemple, l’auditeur peut participer au contexte de transaction de l’éditeur.

2.1. Un événement d'application simple

Créonsa simple event class - juste un espace réservé pour stocker les données d'événement. Dans ce cas, la classe d'événements contient un message String:

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. Un éditeur

Créons maintenanta publisher of that event. L'éditeur construit l'objet événement et le publie à tous ceux qui l'écoutent.

Pour publier l'événement, l'éditeur peut simplement injecter lesApplicationEventPublisher et utiliser l'APIpublishEvent():

@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);
    }
}

Alternativement, la classe d'éditeur peut implémenter l'interfaceApplicationEventPublisherAware - cela injectera également l'éditeur d'événement au démarrage de l'application. En général, il est plus simple d'injecter simplement à l'éditeur@Autowire.

2.3. Un auditeur

Enfin, créons l'auditeur.

La seule exigence pour l'auditeur est d'être un bean et d'implémenter l'interfaceApplicationListener:

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

Remarquez comment notre écouteur personnalisé est paramétré avec le type générique d'événement personnalisé - ce qui rend le type de méthodeonApplicationEvent() sûr. Cela évite également de devoir vérifier si l'objet est une instance d'une classe d'événement spécifique et de le diffuser.

Et, comme déjà discuté - par défautspring events are synchronous - la méthodedoStuffAndPublishAnEvent() se bloque jusqu'à ce que tous les écouteurs aient fini de traiter l'événement.

3. Création d'événements asynchrones

Dans certains cas, la publication d’événements de manière synchrone n’est pas vraiment ce que nous recherchons -we may need async handling of our events.

Vous pouvez activer cela dans la configuration en créant un beanApplicationEventMulticaster avec un exécuteur; pour nos besoins iciSimpleAsyncTaskExecutor fonctionne bien:

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

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

L'événement, l'éditeur et les implémentations de l'écouteur restent les mêmes qu'avant - mais maintenant,the listener will asynchronously deal with the event in a separate thread.

4. Événements du cadre existant

Le printemps lui-même publie une variété d’événements hors de la boîte. Par exemple, lesApplicationContext déclencheront divers événements du framework. E.g. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent etc.

Ces événements offrent aux développeurs d'applications une option pour intégrer le cycle de vie de l'application et le contexte et ajouter leur propre logique personnalisée si nécessaire.

Voici un exemple rapide d'un auditeur à l'écoute des actualisations de contexte:

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

Pour en savoir plus sur les événements de framework existants, jetez un œil àour next tutorial here.

5. Écouteur d'événement basé sur les annotations

Depuis Spring 4.2, un écouteur d'événement n'est pas obligé d'être un bean implémentant l'interfaceApplicationListener - il peut être enregistré sur n'importe quelle méthodepublic d'un bean géré via l'annotation@EventListener:

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

Comme auparavant, la signature de la méthode déclare le type d'événement qu'elle consomme. Comme précédemment, cet écouteur est appelé de manière synchrone. Mais maintenant, le rendre asynchrone est aussi simple que d'ajouter une annotation@Async (n'oubliez pas deenable Async support dans l'application).

6. Prise en charge des génériques

Il est également possible d'envoyer des événements avec des informations génériques dans le type d'événement.

6.1. Un événement d'application générique

Let’s create a generic event type. Dans notre exemple, la classe d'événements contient n'importe quel contenu et un indicateur d'étatsuccess:

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

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

Notez la différence entreGenericSpringEvent etCustomSpringEvent. Nous avons maintenant la flexibilité de publier n'importe quel événement arbitraire et il n'est plus nécessaire d'étendre à partir deApplicationEvent.

6.2. Un auditeur

Créons maintenanta listener of that event. Nous pourrions définir l'auditeur en implémentant l'interface dethe ApplicationListener comme avant:

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

Mais malheureusement, cette définition nous oblige à hériter deGenericSpringEvent de la classeApplicationEvent. Donc, pour ce didacticiel, utilisons un écouteur d'événement basé sur les annotations discutépreviously.

Il est également possible demake the event listener conditional en définissant une expression booléenne SpEL sur l'annotation@EventListener. Dans ce cas, le gestionnaire d'événements ne sera appelé que pour unGenericSpringEvent réussi deString:

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

LeSpring Expression Language (SpEL) est un langage d'expression puissant qui est couvert en détails dans un autre didacticiel.

6.3. Un éditeur

L'éditeur d'événement est similaire à celui décritabove. Mais en raison de la suppression du type, nous devons publier un événement qui résout le paramètre générique sur lequel nous allons filtrer. Par exemple,class GenericStringSpringEvent extends GenericSpringEvent<String>.

Et il y aan alternative way of publishing events. Si nous retournons une valeur non nulle d'une méthode annotée avec@EventListener comme résultat, Spring Framework enverra ce résultat comme un nouvel événement pour nous. De plus, nous pouvons publier plusieurs nouveaux événements en les renvoyant dans une collection à la suite du traitement de l'événement.

7. Événements liés à la transaction

Ce paragraphe concerne l'utilisation de l'annotation@TransactionalEventListener. Pour en savoir plus sur la gestion des transactions, consultez le didacticielTransactions with Spring and JPA.

Depuis Spring 4.2, le framework fournit une nouvelle annotation@TransactionalEventListener, qui est une extension de@EventListener, qui permet de lier l'auditeur d'un événement à une phase de la transaction. La liaison est possible aux phases de transaction suivantes:

  • AFTER_COMMIT (par défaut) est utilisé pour déclencher l'événement si la transaction acompleted successfully

  • AFTER_ROLLBACK - si la transaction arolled back

  • AFTER_COMPLETION - si la transaction acompleted (un alias pourAFTER_COMMIT etAFTER_ROLLBACK)

  • BEFORE_COMMIT est utilisé pour déclencher l'événement à droitebefore transactioncommit

Voici un exemple rapide d'écouteur d'événements transactionnels:

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

Cet écouteur ne sera appelé que s'il existe une transaction dans laquelle le producteur d'événements est en cours d'exécution et est sur le point d'être validée.

Et, si aucune transaction n’est en cours, l’événement n’est pas du tout envoyé à moins que nous ne le remplacions en définissant l’attributfallbackExecution surtrue.

8. Conclusion

Dans ce rapide tutoriel, nous avons passé en revue les bases dedealing with events in Spring - créer un événement personnalisé simple, le publier, puis le gérer dans un écouteur.

Nous avons également examiné brièvement comment activer le traitement asynchrone des événements dans la configuration.

Nous avons ensuite pris connaissance des améliorations introduites dans Spring 4.2, telles que des écouteurs basés sur des annotations, un meilleur support des génériques et des événements liés aux phases de transaction.

Comme toujours, le code présenté dans cet article est disponibleover on Github. Ceci est un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.