CDI bærbar udvidelse og flyvebane

1. Oversigt

I denne vejledning gennemgår vi en interessant funktion i CDI (Context and Dependency Injection) kaldet CDI bærbar udvidelse.

Først begynder vi med at forstå, hvordan det fungerer, og derefter se, hvordan vi skriver en udvidelse. Vi gennemgår trinene til at implementere et CDI-integrationsmodul til Flyway, så vi kan køre en databasemigrering ved opstart af en CDI-container.

Denne vejledning forudsætter en grundlæggende forståelse af CDI. Se denne artikel for en introduktion til CDI.

2. Hvad er en bærbar CDI-udvidelse?

En bærbar CDI-udvidelse er en mekanisme, hvormed vi kan implementere yderligere funktioner oven på CDI-containeren. På bootstrap-tiden scanner CDI-containeren klassestien og opretter metadata om de opdagede klasser.

Under denne scanningsproces affyrer CDI-containeren mange initialiseringshændelser, som kun kan observeres af udvidelser. Det er her, en bærbar CDI-udvidelse kommer i spil.

En CDI Portable-udvidelse observerer disse begivenheder og ændrer eller tilføjer derefter information til metadata oprettet af containeren.

3. Maven-afhængigheder

Lad os starte med at tilføje den krævede afhængighed for CDI API i pom.xml. Det er tilstrækkeligt til at implementere en tom udvidelse.

 javax.enterprise cdi-api 2.0.SP1 

Og til at køre applikationen kan vi bruge enhver kompatibel CDI-implementering. I denne artikel bruger vi Weld-implementeringen.

 org.jboss.weld.se weld-se-core 3.0.5.Final runtime 

Du kan kontrollere, om der er frigivet nye versioner af API'en og implementeringen på Maven Central.

4. Kørsel af flyvebane i et ikke-CDI miljø

Før vi begynder at integrere Flyway og CDI, skal vi først se på, hvordan man kører det i en ikke-CDI-sammenhæng.

Så lad os se på følgende eksempel taget fra det officielle websted for Flyway:

DataSource dataSource = // ... Flyway flyway = new Flyway (); flyway.setDataSource (dataSource); flyway.migrate ();

Som vi kan se, bruger vi kun en Flyway eksempel, der har brug for en Datakilde eksempel.

Vores bærbare CDI-udvidelse vil senere producere Flyway og Datakilde bønner. Med henblik på denne prøve bruger vi en indbygget H2-database, og vi leverer Datakilde egenskaber gennem DataSourceDefinition kommentar.

5. CDI Container Initialization Events

Ved applikationens bootstrap starter CDI-containeren ved at indlæse og instantere alle bærbare CDI-udvidelser. Derefter søger og registrerer den i hver udvidelse observatørmetoder til eventuelle initialiseringshændelser. Derefter udfører den følgende trin:

  1. Brande BeforeBeanDiscovery begivenhed inden scanningen begynder
  2. Udfører den type opdagelse, hvor den scanner arkivbønner, og for hver opdaget type fyrer den ProcessAnnotatedType begivenhed
  3. Brænder AfterTypeDiscovery begivenhed
  4. Udfører bønneopdagelsen
  5. Brænder AfterBeanDiscovery begivenhed
  6. Udfører bønnevalidering og detekterer definitionsfejl
  7. Brænder EfterDeploymentValidation begivenhed

Hensigten med en bærbar CDI-udvidelse er derefter at observere disse begivenheder, kontrollere metadata om de opdagede bønner, ændre disse metadata eller tilføje dem.

I en bærbar CDI-udvidelse kan vi kun observere disse begivenheder.

6. Skrivning af den bærbare CDI-udvidelse

Lad os se, hvordan vi kan tilslutte os nogle af disse begivenheder ved at opbygge vores egen bærbare CDI-udvidelse.

6.1. Implementering af SPI-udbyderen

En bærbar CDI-udvidelse er en Java SPI-udbyder af grænsefladen javax.enterprise.inject.spi.Extension. Se denne artikel for en introduktion til Java SPI.

Først starter vi med at levere Udvidelse implementering. Senere tilføjer vi observatørmetoder til CDI container bootstrap begivenheder:

offentlig klasse FlywayExtension implementerer udvidelse {}

Derefter tilføjer vi et filnavn META-INF / services / javax.enterprise.inject.spi.Extension med dette indhold:

com.baeldung.cdi.extension.FlywayExtension

Som SPI er dette Udvidelse indlæses før container bootstrap. Så observatørmetoder på CDI-bootstrap-begivenhederne kan registreres.

6.2. Definition af observatørmetoder til initialiseringshændelser

I dette eksempel laver vi Flyway klasse, der er kendt af CDI-beholderen, inden scanningen begynder. Dette gøres i registerFlywayType () observatør metode:

offentligt ugyldigt registerFlywayType (@Observes BeforeBeanDiscovery bbdEvent) {bbdEvent.addAnnotatedType (Flyway.class, Flyway.class.getName ()); }

Her har vi tilføjet metadata om Flyway klasse. Fra nu af opfører det sig som om det blev scannet af containeren. Til dette formål har vi brugt addAnnotatedType () metode.

Dernæst observerer vi ProcessAnnotatedType begivenhed at gøre Flyway klasse som en CDI-styret bønne:

public void processAnnotatedType (@Observes ProcessAnnotatedType patEvent) {patEvent.configureAnnotatedType () .add (ApplicationScoped.Literal.INSTANCE) .add (new AnnotationLiteral () {}) .filterMethods (annotatedMethod -> {return annotatedMethod.get). ) == 1 && annotatedMethod.getParameters (). Get (0) .getBaseType () .equals (javax.sql.DataSource.class);}). FindFirst (). Get (). Add (InjectLiteral.INSTANCE); }

Først kommenterer vi Flyway klasse med @ApplicationScoped og @FlywayType kommentarer, så søger vi i Flyway.setDataSource (DataSource dataSource) metode, og vi kommenterer den ved @Indsprøjte.

Det endelige resultat af ovenstående operationer har den samme effekt, som hvis containeren scanner følgende Flyway bønne:

@ApplicationScoped @ FlywayType offentlig klasse Flyway {// ... @Inject public void setDataSource (DataSource dataSource) {// ...}}

Det næste trin er derefter at lave en Datakilde bønne fås til injektion som vores Flyway bønne afhænger af en Datakilde bønne.

For det behandler vi at registrere en Datakilde Bønner ned i beholderen, så bruger vi AfterBeanDiscovery begivenhed:

ugyldig afterBeanDiscovery (@Observes AfterBeanDiscovery abdEvent, BeanManager bm) {abdEvent.addBean () .types (javax.sql.DataSource.class, DataSource.class) .qualifiers (new AnnotationLiteral () {}, new AnnotationLiteral.) scope (ApplicationScoped.class) .name (DataSource.class.getName ()) .beanClass (DataSource.class) .createWith (creationalContext -> {DataSource instance = new DataSource (); instance.setUrl (dataSourceDefinition.url ()); instance.setDriverClassName (dataSourceDefinition.className ()); return instans;}); }

Som vi kan se, har vi brug for en DataSourceDefinition der giver DataSource-egenskaberne.

Vi kan kommentere enhver administreret bønne med følgende kommentar:

@DataSourceDefinition (navn = "ds", className = "org.h2.Driver", url = "jdbc: h2: mem: testdb")

For at udtrække disse egenskaber observerer vi ProcessAnnotatedType begivenhed sammen med @WithAnnotations kommentar:

offentlig ugyldig detectDataSourceDefinition (@Observes @WithAnnotations (DataSourceDefinition.class) ProcessAnnotatedType patEvent) {AnnotatedType at = patEvent.getAnnotatedType (); dataSourceDefinition = at.getAnnotation (DataSourceDefinition.class); }

Og endelig lytter vi til EfterDeployementValidation begivenhed for at få det eftersøgte Flyway bønne fra CDI - beholderen, og påkald derefter migrere() metode:

ugyldigt runFlywayMigration (@Observes AfterDeploymentValidation adv, BeanManager manager) {Flyway flyway = manager.createInstance () .select (Flyway.class, new AnnotationLiteral () {}). get (); flyway.migrate (); }

7. Konklusion

Opbygning af en bærbar CDI-udvidelse synes svært ved første gang, men når vi først har forstået containerinitialiseringslivscyklussen og SPI dedikeret til udvidelser, bliver det et meget kraftfuldt værktøj, som vi kan bruge til at bygge rammer oven på Jakarta EE.

Som normalt kan alle kodeeksempler vist i denne artikel findes på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found