Lente-evenementen

1. Overzicht

In dit artikel bespreken we hoe evenementen in het voorjaar te gebruiken.

Gebeurtenissen zijn een van de meer over het hoofd geziene functionaliteiten in het raamwerk, maar ook een van de nuttiger. En - net als veel andere dingen in Spring - is het publiceren van evenementen een van de mogelijkheden die wordt geboden door ApplicationContext.

Er zijn een paar eenvoudige richtlijnen die u kunt volgen:

  • het evenement zou moeten duren ApplicationEvent
  • de uitgever moet een ApplicationEventPublisher voorwerp
  • de luisteraar moet het ApplicationListener koppel

2. Een aangepast evenement

Met Spring kunnen we aangepaste evenementen maken en publiceren die - standaard - zijn synchroon. Dit heeft een aantal voordelen, zoals bijvoorbeeld dat de luisteraar kan deelnemen aan de transactiecontext van de uitgever.

2.1. Een eenvoudige toepassingsgebeurtenis

Laten we beginnen een simpele event class - slechts een tijdelijke aanduiding om de gebeurtenisgegevens op te slaan. In dit geval bevat de eventklasse een String-bericht:

openbare klasse CustomSpringEvent breidt ApplicationEvent {privé String-bericht uit; openbare CustomSpringEvent (objectbron, tekenreeksbericht) {super (bron); this.message = bericht; } public String getMessage () {retourbericht; }}

2.2. Een uitgever

Laten we nu gaan maken een uitgever van dat evenement. De uitgever construeert het gebeurtenisobject en publiceert het voor iedereen die luistert.

Om het evenement te publiceren, kan de uitgever eenvoudig de ApplicationEventPublisher en gebruik de publishEvent () API:

@Component openbare klasse CustomSpringEventPublisher {@Autowired privé ApplicationEventPublisher applicationEventPublisher; public void publishCustomEvent (laatste String-bericht) {System.out.println ("Publishing custom event."); CustomSpringEvent customSpringEvent = nieuwe CustomSpringEvent (dit, bericht); applicationEventPublisher.publishEvent (customSpringEvent); }}

Als alternatief kan de publisher-klasse het ApplicationEventPublisherAware interface - dit zal ook de evenementuitgever injecteren bij het opstarten van de applicatie. Meestal is het eenvoudiger om de uitgever gewoon te injecteren met @Autowire.

2.3. Een luisteraar

Laten we tot slot de luisteraar maken.

De enige vereiste voor de luisteraar is om een ​​boon te zijn en te implementeren ApplicationListener koppel:

@Component openbare klasse CustomSpringEventListener implementeert ApplicationListener {@Override public void onApplicationEvent (CustomSpringEvent-gebeurtenis) {System.out.println ("Ontvangen aangepaste lente-gebeurtenis -" + event.getMessage ()); }}

Merk op hoe onze aangepaste luisteraar is geparametriseerd met het generieke type aangepaste gebeurtenis - waardoor de onApplicationEvent () methode type-veilig. Dit voorkomt ook dat u moet controleren of het object een instantie is van een specifieke gebeurtenisklasse en het moet casten.

En, zoals al besproken - standaard lente-evenementen zijn synchroon - de doStuffAndPublishAnEvent () method blokken totdat alle luisteraars klaar zijn met het verwerken van de gebeurtenis.

3. Asynchrone gebeurtenissen maken

In sommige gevallen is het synchroon publiceren van evenementen niet echt wat we zoeken - we hebben mogelijk een asynchrone afhandeling van onze evenementen nodig.

U kunt dat in de configuratie inschakelen door een ApplicationEventMulticaster boon met een uitvoerder; voor onze doeleinden hier SimpleAsyncTaskExecutor werkt goed:

@Configuration openbare klasse AsynchronousSpringEventsConfig {@Bean (name = "applicationEventMulticaster") openbare ApplicationEventMulticaster simpleApplicationEventMulticaster () {SimpleApplicationEventMulticaster eventMulticaster = nieuwe SimpleApplicationEventMulticaster (); eventMulticaster.setTaskExecutor (nieuwe SimpleAsyncTaskExecutor ()); terug eventMulticaster; }}

De implementaties van het evenement, de uitgever en de luisteraar blijven hetzelfde als voorheen, maar nu, de luisteraar zal de gebeurtenis asynchroon afhandelen in een aparte thread.

4. Bestaande Framework-evenementen

Spring publiceert zelf een verscheidenheid aan evenementen uit de doos. Bijvoorbeeld de ApplicationContext zal verschillende kaderevenementen afvuren. Bijv. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent enz.

Deze gebeurtenissen bieden applicatieontwikkelaars een optie om in te spelen op de levenscyclus van de applicatie en de context en waar nodig hun eigen aangepaste logica toe te voegen.

Hier is een snel voorbeeld van een luisteraar die luistert naar het vernieuwen van de context:

openbare klasse ContextRefreshedListener implementeert ApplicationListener {@Override public void onApplicationEvent (ContextRefreshedEvent cse) {System.out.println ("Omgaan met opnieuw vernieuwde context."); }}

Bekijk hier onze volgende tutorial voor meer informatie over bestaande framework-evenementen.

5. Annotatiegestuurde event-listener

Vanaf Spring 4.2 hoeft een gebeurtenislistener geen bean te zijn die het ApplicationListener interface - het kan op elk openbaar methode van een beheerde boon via de @EventListener annotatie:

@Component openbare klasse AnnotationDrivenEventListener {@EventListener openbare ongeldige handleContextStart (ContextStartedEvent cse) {System.out.println ("Omgaan met context gestarte gebeurtenis."); }}

Net als voorheen declareert de handtekening van de methode het gebeurtenistype dat het verbruikt.

Standaard wordt de luisteraar synchroon aangeroepen. We kunnen het echter gemakkelijk asynchroon maken door een @Async annotatie. We moeten onthouden om in te schakelen Async ondersteuning in de applicatie.

6. Generieke ondersteuning

Het is ook mogelijk om gebeurtenissen te verzenden met generieke informatie in het gebeurtenistype.

6.1. Een algemene toepassingsgebeurtenis

Laten we een algemeen gebeurtenistype maken. In ons voorbeeld bevat de eventklasse alle inhoud en een succes status indicator:

openbare klasse GenericSpringEvent {privé T wat; beschermd booleaans succes; openbare GenericSpringEvent (T wat, booleaans succes) {this.what = wat; this.success = succes; } // ... standaard getters}

Let op het verschil tussen GenericSpringEvent en CustomSpringEvent. We hebben nu de flexibiliteit om elke willekeurige gebeurtenis te publiceren en het is niet nodig om uit te breiden vanaf ApplicationEvent meer.

6.2. Een luisteraar

Laten we nu gaan maken een luisteraar van die gebeurtenis. We zouden de luisteraar kunnen definiëren door te implementeren de ApplicationListener interface zoals voorheen:

@Component openbare klasse GenericSpringEventListener implementeert ApplicationListener {@Override public void onApplicationEvent (@NonNull GenericSpringEvent-gebeurtenis) {System.out.println ("Ontvangen algemene lente-gebeurtenis -" + event.getWhat ()); }}

Maar helaas vereist deze definitie dat we erven GenericSpringEvent van de ApplicationEvent klasse. Dus laten we voor deze zelfstudie gebruik maken van een annotatiegestuurde gebeurtenislistener die eerder is besproken.

Het is ook mogelijk om maak de gebeurtenislistener voorwaardelijk door een booleaanse SpEL-expressie te definiëren op de @EventListener annotatie. In dit geval wordt de gebeurtenishandler alleen aangeroepen voor een succesvol GenericSpringEvent van Draad:

@Component openbare klasse AnnotationDrivenEventListener {@EventListener (voorwaarde = "# event.success") public void handleSuccessful (GenericSpringEvent-gebeurtenis) {System.out.println ("Afhandeling van generieke gebeurtenis (voorwaardelijk)."); }}

De Spring Expression Language (SpEL) is een krachtige expressietaal die in een andere tutorial in detail wordt behandeld.

6.3. Een uitgever

De uitgever van het evenement is vergelijkbaar met degene die hierboven is beschreven. Maar vanwege het wissen van het type, moeten we een gebeurtenis publiceren die de generieke parameter oplost waarop we zouden filteren. Bijvoorbeeld, klasse GenericStringSpringEvent breidt GenericSpringEvent uit.

En er is een alternatieve manier om evenementen te publiceren. Als we een niet-nulwaarde retourneren van een methode die is geannoteerd met @EventListener als resultaat zal Spring Framework dat resultaat als een nieuw evenement voor ons opsturen. Bovendien kunnen we meerdere nieuwe evenementen publiceren door ze terug te brengen in een verzameling als resultaat van evenementverwerking.

7. Transactie gebonden gebeurtenissen

Deze paragraaf gaat over het gebruik van de @TransactionalEventListener annotatie. Raadpleeg de tutorial Transactions with Spring en JPA voor meer informatie over transactiebeheer.

Sinds Spring 4.2 biedt het framework een nieuw @TransactionalEventListener annotatie, die een extensie is van @EventListener, waarmee de luisteraar van een gebeurtenis kan worden gebonden aan een fase van de transactie. Binding is mogelijk aan de volgende transactiefasen:

  • AFTER_COMMIT (standaard) wordt gebruikt om de gebeurtenis te activeren als de transactie dat heeft gedaan succesvol voltooid
  • AFTER_ROLLBACK - als de transactie heeft terug gerold
  • NA VOLTOOIING - als de transactie heeft voltooid (een alias voor AFTER_COMMIT en AFTER_ROLLBACK)
  • BEFORE_COMMIT wordt gebruikt om het evenement goed af te vuren voordat transactie plegen

Hier is een snel voorbeeld van een transactionele gebeurtenislistener:

@TransactionalEventListener (phase = TransactionPhase.BEFORE_COMMIT) public void handleCustom (CustomSpringEvent-gebeurtenis) {System.out.println ("Verwerkingsgebeurtenis binnen een transactie VOORDAT COMMIT."); }

Deze luisteraar wordt alleen aangeroepen als er een transactie is waarin de gebeurtenisproducent actief is en deze op het punt staat te worden vastgelegd.

En als er geen transactie actief is, wordt de gebeurtenis helemaal niet verzonden, tenzij we dit opheffen door in te stellen fallbackUitvoering toe te schrijven aan waar.

8. Conclusie

In deze korte tutorial hebben we de basisprincipes van omgaan met evenementen in het voorjaar - een eenvoudige aangepaste gebeurtenis maken, deze publiceren en deze vervolgens afhandelen in een luisteraar.

We hebben ook kort bekeken hoe we de asynchrone verwerking van gebeurtenissen in de configuratie kunnen inschakelen.

Vervolgens leerden we over verbeteringen die in Spring 4.2 zijn geïntroduceerd, zoals op aantekeningen gebaseerde luisteraars, betere ondersteuning voor generieke geneesmiddelen en gebeurtenissen die binden aan transactiefasen.

Zoals altijd is de code die in dit artikel wordt gepresenteerd, beschikbaar op Github. Dit is een op Maven gebaseerd project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.