CDI bærbar utvidelse og flyvebane

1. Oversikt

I denne opplæringen vil vi gå over en interessant funksjon av CDI (Context and Dependency Injection) kalt CDI bærbar utvidelse.

Først begynner vi med å forstå hvordan det fungerer, og så får vi se hvordan vi skriver en utvidelse. Vi går gjennom trinnene for å implementere en CDI-integrasjonsmodul for Flyway, slik at vi kan kjøre en databasemigrering ved oppstart av en CDI-container.

Denne opplæringen forutsetter en grunnleggende forståelse av CDI. Ta en titt på denne artikkelen for en introduksjon til CDI.

2. Hva er en bærbar CDI-utvidelse?

En bærbar CDI-utvidelse er en mekanisme der vi kan implementere tilleggsfunksjoner på toppen av CDI-containeren. Ved bootstrap-tiden skanner CDI-containeren klassestien og oppretter metadata om de oppdagede klassene.

Under denne skanneprosessen avfyr CDI-containeren mange initialiseringshendelser som bare kan observeres av utvidelser. Det er her en bærbar CDI-utvidelse kommer til spill.

En bærbar CDI-utvidelse observerer disse hendelsene og endrer eller legger til informasjon i metadataene som er opprettet av containeren.

3. Maven-avhengigheter

La oss starte med å legge til den nødvendige avhengigheten for CDI API i pom.xml. Det er tilstrekkelig for å implementere en tom utvidelse.

 javax.enterprise cdi-api 2.0.SP1 

Og for å kjøre applikasjonen kan vi bruke hvilken som helst kompatibel CDI-implementering. I denne artikkelen bruker vi Weld-implementeringen.

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

Du kan sjekke om noen nye versjoner av API og implementering er utgitt på Maven Central.

4. Kjøre Flyway i et ikke-CDI miljø

Før vi begynner å integrere Flyway og CDI, bør vi først se på hvordan du kjører den i en ikke-CDI-sammenheng.

Så la oss ta en titt på følgende eksempel hentet fra det offisielle nettstedet til Flyway:

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

Som vi ser, bruker vi bare en Flyway forekomst som trenger en Datakilde forekomst.

Vår bærbare CDI-utvidelse vil senere produsere Flyway og Datakilde bønner. For formålet med dette eksemplet vil vi bruke en innebygd H2-database og vi vil gi det Datakilde eiendommer gjennom DataSourceDefinition kommentar.

5. CDI Container Initialization Events

Ved oppstart av applikasjonen starter CDI-beholderen med å laste inn og instantiere alle bærbare CDI-utvidelser. I hver utvidelse søker den og registrerer observatørmetoder for initialiseringshendelser, hvis noen. Deretter utfører den følgende trinn:

  1. Branner BeforeBeanDiscovery hendelsen før skanneprosessen begynner
  2. Utfører typen oppdagelse der den skanner arkivbønner, og for hver oppdaget type fyrer den av ProcessAnnotatedType begivenhet
  3. Avfyrer AfterTypeDiscovery begivenhet
  4. Utfører bønneoppdagelsen
  5. Avfyrer AfterBeanDiscovery begivenhet
  6. Utfører bønnevalidering og oppdager definisjonsfeil
  7. Avfyrer AfterDeploymentValidation begivenhet

Hensikten med en bærbar CDI-utvidelse er å observere disse hendelsene, sjekke metadata om de oppdagede bønnene, endre disse metadataene eller legge til den.

I en bærbar CDI-utvidelse kan vi bare observere disse hendelsene.

6. Skrive CDI Portable Extension

La oss se hvordan vi kan koble oss til noen av disse hendelsene ved å bygge vår egen bærbare CDI-utvidelse.

6.1. Implementering av SPI-leverandøren

En bærbar CDI-utvidelse er en Java SPI-leverandør av grensesnittet javax.enterprise.inject.spi.Extension. Ta en titt på denne artikkelen for en introduksjon til Java SPI.

Først begynner vi med å tilby Utvidelse gjennomføring. Senere vil vi legge til observatørmetoder i CDI-container bootstrap-hendelsene:

offentlig klasse FlywayExtension implementerer utvidelse {}

Deretter legger vi til et filnavn META-INF / services / javax.enterprise.inject.spi.Extension med dette innholdet:

com.baeldung.cdi.extension.FlywayExtension

Som SPI, dette Utvidelse lastes inn før container bootstrap. Så observatørmetoder på CDI bootstrap-hendelsene kan registreres.

6.2. Definere observatørmetoder for initialiseringshendelser

I dette eksemplet lager vi Flyway klasse kjent for CDI-beholderen før skanneprosessen begynner. Dette gjøres i registerFlywayType () observatørmetode:

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

Her har vi lagt til metadata om Flyway klasse. Fra nå av vil den oppføre seg som om den ble skannet av containeren. For dette formålet har vi brukt addAnnotatedType () metode.

Deretter vil vi observere ProcessAnnotatedType hendelse for å lage Flyway klasse som en CDI-styrt 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 merknader, så søker vi i Flyway.setDataSource (DataSource dataSource) metode og vi kommenterer den ved @Injiser.

Det endelige resultatet av ovennevnte operasjoner har samme effekt som om containeren skanner følgende Flyway bønne:

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

Neste trinn er å lage en Datakilde bønne tilgjengelig for injeksjon som vår Flyway bønne avhenger av en Datakilde bønne.

For det vil vi behandle å registrere en Datakilde Bønn i beholderen, så bruker vi AfterBeanDiscovery begivenhet:

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

Som vi kan se, trenger vi en DataSourceDefinition som gir DataSource-egenskapene.

Vi kan kommentere alle administrerte bønner med følgende kommentar:

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

For å trekke ut disse egenskapene observerer vi ProcessAnnotatedType arrangement sammen med @WithAnnotations kommentar:

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

Og til slutt, vi lytter til AfterDeployementValidation hendelse for å få ønsket Flyway bønne fra CDI-beholderen og deretter påberope migrere () metode:

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

7. Konklusjon

Å bygge en bærbar CDI-utvidelse virker vanskelig første gang, men når vi forstår containerens initialiseringssyklus og SPI dedikert til utvidelser, blir det et veldig kraftig verktøy som vi kan bruke til å bygge rammer på toppen av Jakarta EE.

Som vanlig kan alle kodeeksemplene som vises i denne artikkelen finnes på GitHub.


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