CDI Portable Extension en Flyway

1. Overzicht

In deze zelfstudie bespreken we een interessante functie van CDI (Context and Dependency Injection), de draagbare CDI-extensie.

Eerst zullen we beginnen met te begrijpen hoe het werkt, en dan zullen we zien hoe we een extensie kunnen schrijven. We zullen de stappen doorlopen om een ​​CDI-integratiemodule voor Flyway te implementeren, zodat we een databasemigratie kunnen uitvoeren bij het opstarten van een CDI-container.

Deze tutorial veronderstelt een basiskennis van CDI. Bekijk dit artikel voor een inleiding tot CDI.

2. Wat is een draagbare CDI-extensie?

Een draagbare CDI-extensie is een mechanisme waarmee we aanvullende functionaliteiten bovenop de CDI-container kunnen implementeren. Tijdens het opstarten scant de CDI-container het klassenpad en creëert metagegevens over de ontdekte klassen.

Tijdens dit scanproces vuurt de CDI-container veel initialisatiegebeurtenissen af ​​die alleen door extensies kunnen worden waargenomen. Dit is waar een draagbare CDI-extensie in het spel komt.

Een CDI Portable-extensie observeert deze gebeurtenissen en wijzigt of voegt informatie toe aan de metagegevens die door de container zijn gemaakt.

3. Maven afhankelijkheden

Laten we beginnen met het toevoegen van de vereiste afhankelijkheid voor de CDI API in het pom.xml. Het is voldoende om een ​​lege extensie te implementeren.

 javax.enterprise cdi-api 2.0.SP1 

En voor het uitvoeren van de applicatie kunnen we elke compatibele CDI-implementatie gebruiken. In dit artikel gebruiken we de Weld-implementatie.

 org.jboss.weld.se weld-se-core 3.0.5. definitieve looptijd 

U kunt controleren of er nieuwe versies van de API en de implementatie zijn vrijgegeven op Maven Central.

4. Flyway uitvoeren in een niet-CDI-omgeving

Voordat we beginnen met integreren Flyway en CDI, moeten we eerst kijken hoe we het in een niet-CDI-context kunnen uitvoeren.

Laten we dus eens kijken naar het volgende voorbeeld, afkomstig van de officiële site van Flyway:

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

Zoals we kunnen zien, gebruiken we alleen een Flyway instantie waarvoor een Databron voorbeeld.

Onze draagbare CDI-extensie zal later de Flyway en Databron bonen. Voor dit voorbeeld gebruiken we een ingesloten H2-database en zorgen we voor Databron eigenschappen via de DataSourceDefinition annotatie.

5. Initialisatiegebeurtenissen van CDI-container

Bij de bootstrap van de applicatie begint de CDI-container met het laden en instantiëren van alle draagbare CDI-extensies. Vervolgens zoekt en registreert het in elke extensie waarnemingsmethoden van eventuele initialisatiegebeurtenissen. Daarna voert het de volgende stappen uit:

  1. Branden BeforeBeanDiscovery gebeurtenis voordat het scanproces begint
  2. Voert de type-detectie uit waarin het archiefbeans scant, en voor elk ontdekt type vuurt het de ProcessAnnotatedType evenement
  3. Vuurt het AfterTypeDiscovery evenement
  4. Voert de bonenontdekking uit
  5. Vuurt het AfterBeanDiscovery evenement
  6. Voert bean-validatie uit en detecteert definitiefouten
  7. Vuurt het AfterDeploymentValidation evenement

De bedoeling van een draagbare CDI-extensie is dan om deze gebeurtenissen te observeren, metadata over de ontdekte bonen te controleren, deze metadata aan te passen of er iets aan toe te voegen.

In een draagbare CDI-extensie kunnen we deze gebeurtenissen alleen waarnemen.

6. Het schrijven van de draagbare CDI-extensie

Laten we eens kijken hoe we aan een aantal van deze evenementen kunnen deelnemen door onze eigen draagbare CDI-extensie te bouwen.

6.1. Implementatie van de SPI-provider

Een draagbare CDI-extensie is een Java SPI-provider van de interface javax.enterprise.inject.spi.Extension. Bekijk dit artikel voor een inleiding tot Java SPI.

Ten eerste beginnen we met het verstrekken van de Uitbreiding implementatie. Later zullen we waarnemingsmethoden toevoegen aan de CDI-container bootstrap-gebeurtenissen:

openbare klasse FlywayExtension implementeert extensie {}

Vervolgens voegen we een bestandsnaam toe META-INF / services / javax.enterprise.inject.spi.Extension met deze inhoud:

com.baeldung.cdi.extension.FlywayExtension

Als SPI is dit Uitbreiding wordt geladen vóór de container-bootstrap. Dus waarnemingsmethoden op de CDI-bootstrap-evenementen kunnen worden geregistreerd.

6.2. Waarnemingsmethoden voor initialisatiegebeurtenissen definiëren

In dit voorbeeld maken we de Flyway klasse bekend bij de CDI-container voordat het scanproces begint. Dit gebeurt in het registerFlywayType () waarnemer methode:

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

Hier hebben we metadata toegevoegd over het Flyway klasse. Vanaf nu gedraagt ​​het zich alsof het door de container is gescand. Voor dit doel hebben we de addAnnotatedType () methode.

Vervolgens zullen we de ProcessAnnotatedType evenement om de Flyway klasse als een door CDI beheerde bean:

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

Eerst annoteren we de Flyway les met @ApplicationScoped en @FlywayType annotaties, dan zoeken we de Flyway.setDataSource (DataSource dataSource) methode en we annoteren het door @Injecteren.

Het uiteindelijke resultaat van de bovenstaande bewerkingen heeft hetzelfde effect alsof de container het volgende scant Flyway Boon:

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

De volgende stap is dan om een Databron boon beschikbaar voor injectie als onze Flyway boon is afhankelijk van een Databron Boon.

Daarvoor verwerken we het registreren van een Databron Bonen in de container en we gebruiken de AfterBeanDiscovery evenement:

void afterBeanDiscovery (@Observes AfterBeanDiscovery abdEvent, BeanManager bm) {abdEvent.addBean () .types (javax.sql.DataSource.class, DataSource.class) .kwalificaties (nieuwe AnnotationLiteral () {}, nieuwe AnnotationLiteral () {}, nieuwe AnnotationLiteral () {}, nieuwe Annotation }Literal. scope (ApplicationScoped.class) .name (DataSource.class.getName ()) .beanClass (DataSource.class) .createWith (creationalContext -> {DataSource-instantie = nieuwe DataSource (); instance.setUrl (dataSourceDefinition.url ()); instance.setDriverClassName (dataSourceDefinition.className ()); retourinstantie;}); }

Zoals we kunnen zien, hebben we een DataSourceDefinition dat de DataSource-eigenschappen biedt.

We kunnen elke beheerde bean annoteren met de volgende annotatie:

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

Om deze eigenschappen te extraheren, observeren we de ProcessAnnotatedType evenement samen met de @WithAnnotations annotatie:

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

En tot slot luisteren we naar de AfterDeployementValidation evenement om het gewenste te krijgen Flyway bean uit de CDI-container en roep vervolgens de migreren () methode:

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

7. Conclusie

Het bouwen van een draagbare CDI-extensie lijkt de eerste keer moeilijk, maar zodra we de levenscyclus van de containerinitialisatie en de SPI voor extensies begrijpen, wordt het een zeer krachtige tool die we kunnen gebruiken om frameworks te bouwen bovenop Jakarta EE.

Zoals gewoonlijk zijn alle codevoorbeelden die in dit artikel worden getoond, te vinden op GitHub.