Forårsbegivenheder

1. Oversigt

I denne artikel vil vi diskutere hvordan man bruger begivenheder om foråret.

Begivenheder er en af ​​de mere overset funktionaliteter i rammen, men også en af ​​de mere nyttige. Og - ligesom mange andre ting i foråret - er udgivelse af begivenheder en af ​​de muligheder, der leveres af ApplicationContext.

Der er et par enkle retningslinjer at følge:

  • begivenheden skal udvides ApplicationEvent
  • udgiveren skal indsprøjte en ApplicationEventPublisher objekt
  • lytteren skal implementere ApplicationListener interface

2. En brugerdefineret begivenhed

Foråret giver os mulighed for at oprette og udgive tilpassede begivenheder, som - som standard - er synkrone. Dette har nogle få fordele - f.eks. At lytteren kan deltage i udgiverens transaktionssammenhæng.

2.1. En simpel applikationsbegivenhed

Lad os oprette en simpel begivenhedsklasse - bare en pladsholder til at gemme begivenhedsdataene. I dette tilfælde har begivenhedsklassen en strengbesked:

offentlig klasse CustomSpringEvent udvider ApplicationEvent {privat streng besked; offentlig CustomSpringEvent (objektkilde, strengmeddelelse) {super (kilde); denne besked = besked; } public String getMessage () {return message; }}

2.2. En udgiver

Lad os nu oprette en udgiver af begivenheden. Forlaget konstruerer begivenhedsobjektet og udgiver det til alle, der lytter.

For at udgive begivenheden kan udgiveren simpelthen indsprøjte ApplicationEventPublisher og brug publishEvent () API:

@Komponent offentlig klasse CustomSpringEventPublisher {@Autowired private ApplicationEventPublisher applicationEventPublisher; public void publishCustomEvent (final String message) {System.out.println ("Publishing custom event."); CustomSpringEvent customSpringEvent = ny CustomSpringEvent (denne meddelelse); applicationEventPublisher.publishEvent (customSpringEvent); }}

Alternativt kan forlagsklassen implementere ApplicationEventPublisherAware interface - dette vil også indsprøjte begivenhedsudgiveren til start af applikationen. Normalt er det lettere at bare injicere udgiveren med @Autowire.

2.3. En lytter

Lad os endelig oprette lytteren.

Det eneste krav til lytteren er at være en bønne og implementere ApplicationListener grænseflade:

@Komponent offentlig klasse CustomSpringEventListener implementerer ApplicationListener {@Override public void onApplicationEvent (CustomSpringEvent event) {System.out.println ("Modtaget foråret tilpasset begivenhed -" + event.getMessage ()); }}

Læg mærke til, hvordan vores brugerdefinerede lytter parametriseres med den generiske type brugerdefineret begivenhed - hvilket gør onApplicationEvent () metode typesikker. Dette undgår også at skulle kontrollere, om objektet er en forekomst af en bestemt begivenhedsklasse og caste det.

Og som allerede diskuteret - som standard forårshændelser er synkrone - det doStuffAndPublishAnEvent () metode blokerer, indtil alle lyttere er færdige med at behandle begivenheden.

3. Oprettelse af asynkrone begivenheder

I nogle tilfælde er offentliggørelse af begivenheder synkront ikke det, vi leder efter - vi har muligvis brug for asynkronisering af vores begivenheder.

Du kan slå det til i konfigurationen ved at oprette en ApplicationEventMulticaster bønne med en eksekutor; til vores formål her SimpleAsyncTaskExecutor fungerer godt:

@Configuration public class AsynchronousSpringEventsConfig {@Bean (name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster () {SimpleApplicationEventMulticaster eventMulticaster = ny SimpleApplicationEventMulticaster (); eventMulticaster.setTaskExecutor (ny SimpleAsyncTaskExecutor ()); returevent Multicaster; }}

Begivenheden, udgiveren og lytterimplementeringerne forbliver de samme som før - men nu, lytteren vil asynkront behandle begivenheden i en separat tråd.

4. Eksisterende rammebegivenheder

Spring selv udgiver en række begivenheder ud af kassen. F.eks ApplicationContext vil affyre forskellige rammebegivenheder. F.eks. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent etc.

Disse begivenheder giver applikationsudviklere mulighed for at tilslutte sig applikationens livscyklus og konteksten og tilføje deres egen brugerdefinerede logik, hvor det er nødvendigt.

Her er et hurtigt eksempel på en lytter, der lytter til kontekstopdateringer:

offentlig klasse ContextRefreshedListener implementerer ApplicationListener {@Override public void onApplicationEvent (ContextRefreshedEvent cse) {System.out.println ("Håndtering af kontekst genopfrisket begivenhed."); }}

For at lære mere om eksisterende rammebegivenheder, se vores næste vejledning her.

5. Annoteringsdrevet begivenhedslytter

Fra og med foråret 4.2 er en begivenhedslytter ikke forpligtet til at være en bønne, der implementerer ApplicationListener interface - det kan registreres på enhver offentlig metode til en styret bønne via @EventListener kommentar:

@Component public class AnnotationDrivenEventListener {@EventListener public void handleContextStart (ContextStartedEvent cse) {System.out.println ("Håndtering af kontekst startet begivenhed."); }}

Som tidligere erklærer metodesignaturen den begivenhedstype, den bruger.

Som standard påkaldes lytteren synkront. Vi kan dog let gøre det asynkront ved at tilføje en @Async kommentar. Vi skal huske at aktivere Async støtte i applikationen, dog.

6. Generisk support

Det er også muligt at sende begivenheder med generiske oplysninger i hændelsestypen.

6.1. En generel applikationsbegivenhed

Lad os oprette en generisk begivenhedstype. I vores eksempel indeholder begivenhedsklassen alt indhold og en succes statusindikator:

offentlig klasse GenericSpringEvent {privat T hvad; beskyttet boolsk succes public GenericSpringEvent (T hvad, boolsk succes) {this.what = what; this.success = succes; } // ... standard getters}

Læg mærke til forskellen mellem GenericSpringEvent og CustomSpringEvent. Vi har nu fleksibiliteten til at offentliggøre en vilkårlig begivenhed, og det er ikke nødvendigt at forlænge den fra ApplicationEvent længere.

6.2. En lytter

Lad os nu oprette en lytter til begivenheden. Vi kunne definere lytteren ved at implementere ApplicationListener interface som før:

@Komponent offentlig klasse GenericSpringEventListener implementerer ApplicationListener {@Override public void onApplicationEvent (@NonNull GenericSpringEvent event) {System.out.println ("Modtaget generisk forårshændelse -" + event.getWhat ()); }}

Men desværre kræver denne definition, at vi arver GenericSpringEvent fra ApplicationEvent klasse. Så til denne vejledning, lad os bruge en annoteringsdrevet begivenhedslytter, der er diskuteret tidligere.

Det er også muligt at gør begivenhedslytteren betinget ved at definere et boolsk SpEL-udtryk på @EventListener kommentar. I dette tilfælde vil begivenhedshåndtereren kun blive påberåbt for en succes GenericSpringEvent af Snor:

@Component public class AnnotationDrivenEventListener {@EventListener (condition = "# event.success") public void handleSuccessful (GenericSpringEvent event) {System.out.println ("Håndtering af generisk begivenhed (betinget)."); }}

Spring Expression Language (SpEL) er et stærkt ekspressionssprog, der er dækket detaljeret i en anden tutorial.

6.3. En udgiver

Begivenhedsudgiveren ligner den, der er beskrevet ovenfor. Men på grund af type sletning er vi nødt til at offentliggøre en begivenhed, der løser den generiske parameter, vi ville filtrere på. For eksempel, klasse GenericStringSpringEvent udvider GenericSpringEvent.

Og der er en alternativ måde at offentliggøre begivenheder på. Hvis vi returnerer en ikke-nulværdi fra en metode, der er kommenteret med @EventListener som et resultat vil Spring Framework sende resultatet som en ny begivenhed for os. Desuden kan vi offentliggøre flere nye begivenheder ved at returnere dem i en samling som et resultat af behandling af begivenheder.

7. Transaktionsbundne begivenheder

Dette afsnit handler om at bruge @TransactionalEventListener kommentar. For at lære mere om transaktionsstyring, se vejledningen Transaktioner med forår og JPA.

Siden foråret 4.2 giver rammen en ny @TransactionalEventListener kommentar, som er en udvidelse af @EventListener, der gør det muligt at binde lytteren af ​​en begivenhed til en fase af transaktionen. Binding er mulig til følgende transaktionsfaser:

  • AFTER_COMMIT (standard) bruges til at udløse begivenheden, hvis transaktionen har fundet sted gennemført med succes
  • AFTER_ROLLBACK - hvis transaktionen har fundet sted rullede tilbage
  • AFTER_COMPLETION - hvis transaktionen har afsluttet (et alias for AFTER_COMMIT og AFTER_ROLLBACK)
  • FØR_COMMIT bruges til at fyre begivenheden rigtigt Før transaktion begå

Her er et hurtigt eksempel på lytter til transaktionsbegivenheder:

@TransactionalEventListener (phase = TransactionPhase.BEFORE_COMMIT) public void handleCustom (CustomSpringEvent event) {System.out.println ("Håndtering af begivenhed i en transaktion FØR ENGAGEMENT."); }

Denne lytter påkaldes kun, hvis der er en transaktion, hvor begivenhedsproducenten kører, og den er ved at blive begået.

Og hvis ingen transaktion kører, sendes begivenheden slet ikke, medmindre vi tilsidesætter dette ved at indstille udførelse attribut til rigtigt.

8. Konklusion

I denne hurtige vejledning gik vi over det grundlæggende i beskæftiger sig med begivenheder i foråret - oprettelse af en simpel brugerdefineret begivenhed, offentliggørelse og derefter håndtering af den i en lytter.

Vi havde også et kort kig på, hvordan man aktiverer asynkron behandling af begivenheder i konfigurationen.

Derefter lærte vi om forbedringer, der blev introduceret i foråret 4.2, såsom annoteringsdrevne lyttere, bedre generisk support og begivenheder, der er bindende for transaktionsfaser.

Som altid er koden præsenteret i denne artikel tilgængelig på Github. Dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.