Proljetni događaji

1. Pregled

U ovom ćemo članku raspravljati kako koristiti događaje u proljeće.

Događaji su jedna od funkcija koja se više ne zanemaruje, ali i jedna od korisnijih. I - kao i mnoge druge stvari u proljeće - objavljivanje događaja jedna je od mogućnosti koju pruža ApplicationContext.

Treba slijediti nekoliko jednostavnih smjernica:

  • događaj bi se trebao produžiti ApplicationEvent
  • izdavač bi trebao ubrizgati ApplicationEventPublisher objekt
  • slušatelj bi trebao implementirati ApplicationListener sučelje

2. Prilagođeni događaj

Proljeće nam omogućuje stvaranje i objavljivanje prilagođenih događaja koji - prema zadanim postavkama - su sinkroni. To ima nekoliko prednosti - kao što je, na primjer, slušatelj koji može sudjelovati u kontekstu transakcije izdavača.

2.1. Jednostavan aplikacijski događaj

Stvorimo jednostavan tečaj događaja - samo rezervirano mjesto za pohranu podataka o događaju. U ovom slučaju, klasa događaja sadrži String poruku:

javna klasa CustomSpringEvent proširuje ApplicationEvent {private String message; javni CustomSpringEvent (Izvor objekta, String poruka) {super (izvor); this.message = poruka; } javni String getMessage () {return poruka; }}

2.2. Izdavač

Ajmo sada stvarati izdavač tog događaja. Izdavač konstruira objekt događaja i objavljuje ga svima koji slušaju.

Da bi objavio događaj, izdavač može jednostavno ubrizgati ApplicationEventPublisher i koristite objavitiDogađaj () API:

@Component javna klasa CustomSpringEventPublisher {@Autowired private ApplicationEventPublisher applicationEventPublisher; public void objavitiCustomEvent (konačna niz poruka) {System.out.println ("Objavljivanje prilagođenog događaja."); CustomSpringEvent customSpringEvent = novi CustomSpringEvent (ovo, poruka); applicationEventPublisher.publishEvent (customSpringEvent); }}

Alternativno, klasa izdavača može implementirati ApplicationEventPublisherAware sučelje - to će također ubrizgati izdavača događaja prilikom pokretanja aplikacije. Obično je jednostavnije ubrizgati izdavača @Autowire.

2.3. Slušatelj

Napokon, stvorimo slušatelja.

Jedini uvjet za slušatelja je biti grah i primijeniti ApplicationListener sučelje:

@Component javna klasa CustomSpringEventListener implementira ApplicationListener {@Override public void onApplicationEvent (CustomSpringEvent event) {System.out.println ("Primljeni prilagođeni proljetni događaj -" + event.getMessage ()); }}

Primijetite kako je naš prilagođeni slušatelj parametriziran s generičkim tipom prilagođenog događaja - što čini onApplicationEvent () metoda sigurna za tip. Time se također izbjegava provjeravati je li objekt primjerak određene klase događaja i emitirati ga.

I, kao što je već rečeno - prema zadanim postavkama proljetni događaji su sinkroni - the doStuffAndPublishAnEvent () metoda blokira dok svi slušatelji ne završe obradu događaja.

3. Stvaranje asinkronih događaja

U nekim slučajevima sinkrono objavljivanje događaja zapravo nije ono što tražimo - možda će nam trebati asinkrono rukovanje našim događajima.

To možete uključiti u konfiguraciji izradom datoteke ApplicationEventMulticaster grah s izvršiteljem; za naše svrhe ovdje SimpleAsyncTaskExecutor radi dobro:

@Configuration javna klasa AsynchronousSpringEventsConfig {@Bean (name = "applicationEventMulticaster") public ApplicationEventMulticaster simpleApplicationEventMulticaster () {SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster (new SimpleApplicationEventMulticaster) eventMulticaster.setTaskExecutor (novi SimpleAsyncTaskExecutor ()); povrat događajaMulticaster; }}

Implementacije događaja, izdavača i slušatelja ostaju iste kao i prije - ali sada, slušatelj će se asinkrono baviti događajem u zasebnoj niti.

4. Postojeći okvirni događaji

Proljeće samo po sebi objavljuje razne događaje. Na primjer, ApplicationContext ispalit će razne okvirne događaje. Npr. ContextRefreshedEvent, ContextStartedEvent, RequestHandledEvent itd.

Ti događaji pružaju programerima aplikacija mogućnost da se uključe u životni ciklus aplikacije i kontekst i dodaju u vlastitu prilagođenu logiku po potrebi.

Evo kratkog primjera slušatelja koji osluškuje kontekst:

javna klasa ContextRefreshedListener implementira ApplicationListener {@Override public void onApplicationEvent (ContextRefreshedEvent cse) {System.out.println ("Rukovanje kontekstom osvježenim događajem."); }}

Da biste saznali više o postojećim okvirnim događajima, pogledajte sljedeći vodič ovdje.

5. Slušatelj događaja vođen bilješkama

Počevši od proljeća 4.2, slušatelj događaja ne mora biti grah koji implementira ApplicationListener sučelje - može se registrirati na bilo kojem javnost metoda upravljanog graha putem @EventListener napomena:

@Component javna klasa AnnotationDrivenEventListener {@EventListener javna void handleContextStart (ContextStartedEvent cse) {System.out.println ("Rukovanje kontekstom započetim događajem."); }}

Kao i prije, potpis metode deklarira vrstu događaja koju troši.

Prema zadanim postavkama, slušatelj se poziva sinkrono. Međutim, lako ga možemo učiniti asinkronim dodavanjem @Async bilješka. Moramo se sjetiti omogućiti Async podrška u aplikaciji.

6. Generička podrška

Također je moguće poslati događaje s generičkim informacijama u vrsti događaja.

6.1. Generički događaj prijave

Stvorimo generički tip događaja. U našem primjeru klasa događaja sadrži bilo koji sadržaj i a uspjeh indikator statusa:

javna klasa GenericSpringEvent {private T what; zaštićeni logički uspjeh; javni GenericSpringEvent (T what, logički uspjeh) {this.what = what; this.success = uspjeh; } // ... standardni getteri}

Primijetite razliku između GenericSpringEvent i CustomSpringEvent. Sada imamo fleksibilnost za objavljivanje bilo kojeg proizvoljnog događaja i nije potrebno produžiti od ApplicationEvent više.

6.2. Slušatelj

Ajmo sada stvarati slušatelj toga događaja. Slušatelja bismo mogli definirati primjenom ApplicationListener sučelje kao prije:

@Component javna klasa GenericSpringEventListener implementira ApplicationListener {@Override public void onApplicationEvent (@NonNull GenericSpringEvent event) {System.out.println ("Primljeni općeniti proljetni događaj -" + event.getWhat ()); }}

Ali nažalost, ova definicija zahtijeva nasljeđivanje GenericSpringEvent od ApplicationEvent razred. Dakle, za ovaj vodič, iskoristimo slušatelj događaja vođen bilješkama o kojem smo ranije razgovarali.

Također je moguće uvjetovati slušatelja događaja definiranjem logičkog izraza SpEL na @EventListener bilješka. U tom će se slučaju obrađivač događaja pozivati ​​samo za uspješno GenericSpringEvent od Niz:

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

Spring Expression Language (SpEL) moćan je izrazni jezik koji je detaljno obrađen u drugom uputstvu.

6.3. Izdavač

Izdavač događaja sličan je gore opisanom. Ali zbog brisanja tipa, moramo objaviti događaj koji rješava generički parametar koji bismo filtrirali. Na primjer, klasa GenericStringSpringEvent proširuje GenericSpringEvent.

I tu je alternativni način objavljivanja događaja. Ako vratimo ne-null vrijednost iz metode označene s @EventListener kao rezultat, Spring Framework će poslati taj rezultat kao novi događaj za nas. Štoviše, možemo objaviti više novih događaja vraćajući ih u zbirku kao rezultat obrade događaja.

7. Transakcijski vezani događaji

Ovaj odlomak govori o korištenju @TransactionalEventListener bilješka. Da biste saznali više o upravljanju transakcijama, pogledajte udžbenik Transakcije s proljećem i JPA.

Od proljeća 4.2., Okvir pruža novo @TransactionalEventListener napomena, koja je produžetak @EventListener, koji omogućuje vezivanje slušatelja događaja za fazu transakcije. Vezivanje je moguće za sljedeće faze transakcija:

  • NAKON_DOSELJENJA (zadano) koristi se za aktiviranje događaja ako transakcija ima uspješno dovršen
  • NAKON_POVRATKA - ako transakcija ima otkotrljali natrag
  • NAKON_IZVRŠENJA - ako transakcija ima dovršen (alias za NAKON_DOSELJENJA i NAKON_POVRATKA)
  • PRIJE_COMMIT koristi se za pravo pokretanje događaja prije transakcija počiniti

Evo kratkog primjera slušatelja transakcijskih događaja:

@TransactionalEventListener (faza = TransactionPhase.BEFORE_COMMIT) javna praznina handleCustom (CustomSpringEvent događaj) {System.out.println ("Rukovanje događajem unutar transakcije PRE OBAVEZE."); }

Ovaj će se slušatelj pozvati samo ako postoji transakcija u kojoj se pokreće proizvođač događaja i koja će uskoro biti predana.

Ako se ne izvršava nijedna transakcija, događaj se uopće ne šalje, osim ako to poništimo postavljanjem rezervno izvršenje pripisati pravi.

8. Zaključak

U ovom smo brzom uputstvu prešli na osnove bavljenje događajima u proljeće - stvaranje jednostavnog prilagođenog događaja, objavljivanje i obrada u slušatelju.

Također smo imali kratki uvid u to kako omogućiti asinkronu obradu događaja u konfiguraciji.

Tada smo saznali o poboljšanjima uvedenim u proljeće 4.2, poput slušatelja vođenih bilješkama, bolje generičke podrške i događaja koji se vežu za faze transakcija.

Kao i uvijek, kôd predstavljen u ovom članku dostupan je na Githubu. Ovo je projekt zasnovan na Mavenu, pa bi ga trebalo lako uvesti i pokrenuti kakav jest.