Inleiding tot het Event Notification Model in CDI 2.0

1. Overzicht

CDI (Contexts and Dependency Injection) is het standaard framework voor afhankelijkheidsinjectie van het Jakarta EE-platform.

In deze tutorial bekijken we CDI 2.0 en hoe het voortbouwt op het krachtige, typeveilige injectiemechanisme van CDI 1.x door het toevoegen van een verbeterd, volledig uitgerust meldingsmodel voor gebeurtenissen.

2. De afhankelijkheden van Maven

Om te beginnen bouwen we een eenvoudig Maven-project.

We hebben een CDI 2.0-compatibele container nodig, en Weld, de referentie-implementatie van CDI, past goed bij:

  javax.enterprise cdi-api 2.0.SP1 org.jboss.weld.se weld-se-core 3.0.5.Final 

Zoals gewoonlijk kunnen we de nieuwste versies van cdi-api en las-se-kern van Maven Central.

3. Observeren en afhandelen van aangepaste gebeurtenissen

Simpel gezegd, het CDI 2.0-gebeurtenismeldingsmodel is een klassieke implementatie van het Observer-patroon, gebaseerd op de @Observeert methode-parameter annotatie. Daarom kunnen we gemakkelijk waarnemingsmethoden definiëren, die automatisch kunnen worden opgeroepen als reactie op een of meer gebeurtenissen.

We zouden bijvoorbeeld een of meer bonen kunnen definiëren, die een of meer specifieke gebeurtenissen zouden activeren, terwijl andere bonen op de hoogte zouden worden gebracht van de gebeurtenissen en dienovereenkomstig zouden reageren.

Om duidelijker te demonstreren hoe dit werkt, zullen we een eenvoudig voorbeeld bouwen, inclusief een basisserviceklasse, een aangepaste gebeurtenisklasse en een waarnemingsmethode die reageert op onze aangepaste gebeurtenissen.

3.1. Een basis serviceklasse

Laten we beginnen met het maken van een eenvoudig TextService klasse:

openbare klasse TextService {openbare String parseText (String-tekst) {return text.toUpperCase (); }} 

3.2. Een aangepaste evenementklasse

Laten we vervolgens een voorbeeldgebeurtenisklasse definiëren, die een Draad argument in zijn constructor:

openbare klasse ExampleEvent {private final String eventMessage; openbare ExampleEvent (String eventMessage) {this.eventMessage = eventMessage; } // getter}

3.3. Een waarnemingsmethode definiëren met de @Observeert Annotatie

Nu we onze service- en evenementklassen hebben gedefinieerd, gaan we de @Observeert annotatie om een ​​waarnemingsmethode voor onze VoorbeeldEvent klasse:

openbare klasse ExampleEventObserver {openbare String onEvent (@Observes ExampleEvent-gebeurtenis, TextService textService) {return textService.parseText (event.getEventMessage ()); }}

Terwijl op het eerste gezicht de implementatie van de onEvent () methode ziet er vrij triviaal uit, het bevat eigenlijk veel functionaliteit via de @Observeert annotatie.

Zoals we kunnen zien, is de onEvent () methode is een gebeurtenishandler die VoorbeeldEvent en TextService objecten als argumenten.

Houd er rekening mee dat alle argumenten die na de @Observeert annotatie zijn standaard injectiepunten. Als gevolg hiervan zal CDI volledig geïnitialiseerde instanties voor ons maken en deze in de observer-methode injecteren.

3.4. Initialiseren van onze CDI 2.0-container

Op dit punt hebben we onze service- en evenementklassen gemaakt en we hebben een eenvoudige waarnemingsmethode gedefinieerd om op onze evenementen te reageren. Maar hoe instrueren we CDI om deze instanties tijdens runtime te injecteren?

Hier laat het gebeurtenismeldingsmodel zijn functionaliteit ten volle zien. We initialiseren gewoon het nieuwe SeContainer implementatie en start een of meer evenementen via de fireEvent () methode:

SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance (); probeer (SeContainer container = containerInitializer.initialize ()) {container.getBeanManager (). fireEvent (nieuw ExampleEvent ("Welkom bij Baeldung!")); }

Merk op dat we de SeContainerInitializer en SeContainer objecten omdat we CDI gebruiken in een Java SE-omgeving, in plaats van in Jakarta EE.

Alle aangesloten waarnemermethoden worden op de hoogte gebracht wanneer het VoorbeeldEvent wordt afgevuurd door de gebeurtenis zelf te verspreiden.

Omdat alle objecten zijn doorgegeven als argumenten na de @Observeert annotatie wordt volledig geïnitialiseerd, CDI zorgt voor de bedrading van het geheel TextService objectgrafiek voor ons, voordat u deze in de onEvent () methode.

In een notendop, we hebben de voordelen van een typeveilige IoC-container, samen met een functierijk model voor gebeurtenismeldingen.

4. Het Container geïnitialiseerd Evenement

In het vorige voorbeeld hebben we een aangepaste gebeurtenis gebruikt om een ​​gebeurtenis door te geven aan een waarnemingsmethode en een volledig geïnitialiseerd TextService voorwerp.

Dit is natuurlijk handig als we echt een of meer gebeurtenissen over meerdere punten van onze applicatie moeten verspreiden.

Soms hebben we gewoon een aantal volledig geïnitialiseerde objecten nodig die klaar zijn om te worden gebruikt in onze applicatieklassen, zonder dat u door de implementatie van extra evenementen hoeft te gaan.

Daartoe CDI 2.0 biedt de Container geïnitialiseerd event-klasse, die automatisch wordt geactiveerd wanneer de Weld-container wordt geïnitialiseerd.

Laten we eens kijken hoe we de Container geïnitialiseerd gebeurtenis voor het overdragen van de controle aan de VoorbeeldEventObserver klasse:

openbare klasse ExampleEventObserver {openbare String onEvent (@Observes ContainerInitialized-gebeurtenis, TextService textService) {return textService.parseText (event.getEventMessage ()); }} 

En houd daar rekening mee de Container geïnitialiseerd event class is Weld-specifiek. We zullen dus onze waarnemingsmethoden moeten herstructureren als we een andere CDI-implementatie gebruiken.

5. Voorwaardelijke waarnemingsmethoden

In de huidige implementatie is onze VoorbeeldEventObserver class definieert standaard een onvoorwaardelijke waarnemersmethode. Dit betekent dat de waarnemersmethode wordt altijd op de hoogte gebracht van de aangeleverde gebeurtenis, ongeacht of een instantie van de klasse in de huidige context bestaat of niet.

Evenzo kunnen we definieer een voorwaardelijke waarnemingsmethode door te specificeren meldObserver = IF_EXISTS als argument voor de @Observeert annotatie:

openbare String onEvent (@Observes (notificationObserver = IF_EXISTS) ExampleEvent-gebeurtenis, TextService textService) {return textService.parseText (event.getEventMessage ()); } 

Wanneer we een voorwaardelijke waarnemersmethode gebruiken, de methode wordt alleen op de hoogte gebracht van de matchingsgebeurtenis als een instantie van de klasse die de observer-methode definieert, in de huidige context bestaat.

6. Transactionele waarnemersmethoden

We kunnen ook gebeurtenissen binnen een transactie activeren, zoals een database-update of verwijderingsoperatie. Om dit te doen, kunnen we transactionele waarnemingsmethoden definiëren door de toevoeging gedurende argument aan de @Observeert annotatie.

Elke mogelijke waarde van de gedurende argument komt overeen met een bepaalde fase van een transactie:

  • BEFORE_COMPLETION
  • NA VOLTOOIING
  • NA_SUCCES
  • AFTER_FAILURE

Als we het VoorbeeldEvent gebeurtenis binnen een transactie, moeten we de onEvent () methode dienovereenkomstig om de gebeurtenis tijdens de vereiste fase af te handelen:

openbare String onEvent (@Observes (tijdens = AFTER_COMPLETION) ExampleEvent-gebeurtenis, TextService textService) {return textService.parseText (event.getEventMessage ()); }

Een transactionele waarnemersmethode wordt alleen op de hoogte gebracht van de geleverde gebeurtenis in de matchingfase van een bepaalde transactie.

7. Waarnemingsmethoden bestellen

Een andere mooie verbetering in het gebeurtenismeldingsmodel van CDI 2.0 is de mogelijkheid om een ​​volgorde of prioriteit in te stellen voor het oproepen van waarnemers van een bepaalde gebeurtenis.

We kunnen eenvoudig de volgorde bepalen waarin de waarnemermethoden worden aangeroepen door de @Prioriteit annotatie na @Observeert.

Laten we, om te begrijpen hoe deze functie werkt, een andere waarnemingsmethode definiëren, afgezien van degene die VoorbeeldEventObserver implementeert:

openbare klasse AnotherExampleEventObserver {openbare String onEvent (@Observes ExampleEvent-gebeurtenis) {return event.getEventMessage (); }}

In dit geval hebben beide waarnemersmethoden standaard dezelfde prioriteit. De volgorde waarin CDI ze zal aanroepen, is dus eenvoudigweg onvoorspelbaar.

We kunnen dit eenvoudig oplossen door aan elke methode een aanroepprioriteit toe te kennen via de @Prioriteit annotatie:

public String onEvent (@Observes @Priority (1) ExampleEvent-gebeurtenis, TextService textService) {// ... implementatie} 
public String onEvent (@Observes @Priority (2) ExampleEvent-gebeurtenis) {// ... implementatie}

Prioriteitsniveaus volgen een natuurlijke volgorde. Daarom zal CDI eerst de observer-methode aanroepen met een prioriteitsniveau van 1 en zal als tweede de methode aanroepen met een prioriteitsniveau van 2.

Hetzelfde, als we hetzelfde prioriteitsniveau gebruiken voor twee of meer methoden, is de volgorde opnieuw ongedefinieerd.

8. Asynchrone gebeurtenissen

In alle voorbeelden die we tot nu toe hebben geleerd, hebben we gebeurtenissen synchroon afgevuurd. Met CDI 2.0 kunnen we echter ook gemakkelijk asynchrone events afvuren. Asynchrone waarnemingsmethoden kunnen dat dan afhandelen van deze asynchrone gebeurtenissen in verschillende threads.

We kunnen een evenement asynchroon activeren met de fireAsync () methode:

openbare klasse ExampleEventSource {@Inject Event exampleEvent; public void fireEvent () {exampleEvent.fireAsync (nieuwe ExampleEvent ("Welkom bij Baeldung!")); }}

Bonen activeren evenementen, die implementaties zijn van de Evenement koppel. Daarom kunnen we ze injecteren als elke andere conventionele boon.

Om onze asynchrone gebeurtenis af te handelen, moeten we een of meer asynchrone waarnemermethoden definiëren met de @ObservesAsync annotatie:

openbare klasse AsynchronousExampleEventObserver {public void onEvent (@ObservesAsync ExampleEvent-gebeurtenis) {// ... implementatie}}

9. Conclusie

In dit artikel, we hebben geleerd hoe we aan de slag kunnen gaan met het verbeterde model voor gebeurteniskennisgeving gebundeld met CDI 2.0.

Zoals gewoonlijk zijn alle codevoorbeelden die in deze tutorial worden getoond, beschikbaar op GitHub.