1概要

このチュートリアルでは、CDIポータブルエクステンションと呼ばれるCDI(Context and Dependency Injection)の興味深い機能について説明します。

まず、それがどのように機能するのかを理解することから始め、次にエクステンションの書き方を見ます。 Flyway用のCDI統合モジュールを実装するための手順を実行するので、CDIコンテナの起動時にデータベースの移行を実行できます。

このチュートリアルはCDIの基本的な知識を前提としています。 CDIの概要については、https://www.baeldung.com/java-ee-cdi[この記事]をご覧ください。

2. CDI Portable Extensionとは何ですか?

CDIポータブルエクステンションは、CDIコンテナの上に追加の機能を実装できるメカニズムです。ブートストラップ時に、CDIコンテナはクラスパスをスキャンし、検出されたクラスに関するメタデータを作成します。

  • このスキャンプロセスの間に、CDIコンテナは多くの初期化イベントを発生させます。これらは拡張機能によってのみ観察されます。これがCDIポータブルエクステンションが登場するところです。

CDI Portable拡張機能はこれらのイベントを監視してから、コンテナーによって作成されたメタデータの情報を変更または追加します。


3 Mavenの依存関係

CDI APIに必要な依存関係を

pom.xml

に追加することから始めましょう。空のエクステンションを実装するのに十分です。

<dependency>
    <groupId>javax.enterprise</groupId>
    <artifactId>cdi-api</artifactId>
    <version>2.0.SP1</version>
</dependency>

そしてアプリケーションを実行するために、私たちはどんな準拠CDI実装を使うこともできます。この記事では、Weld実装を使用します。

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-core</artifactId>
    <version>3.0.5.Final</version>
    <scope>runtime</scope>
</dependency>


theの新しいバージョンがあるかどうかを確認できます。 API

およびhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.jboss.weld.se%22%20AND%20a%3A%22weld-se-core%22[実装]はMaven Centralでリリースされました。


4非CDI環境でのFlywayの実行


Flyway

とCDIの統合を始める前に、まず非CDIコンテキストでそれを実行する方法を検討する必要があります。

それでは、https://flywaydb.org/getstarted/firststeps/api[公式サイト

Flyway

]____から抜粋した次の例を見てみましょう。

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

ご覧のとおり、

DataSource

インスタンスが必要な

Flyway

インスタンスのみを使用しています。

私たちのCDIポータブルエクステンションは後に

Flyway



Datasource

Beanを生成します。このサンプルでは、​​埋め込みH2データベースを使用し、

DataSourceDefinition

アノテーションを通じて

DataSource

プロパティを提供します。


5 CDIコンテナ初期化イベント

アプリケーションのブートストラップでは、CDIコンテナはすべてのCDIポータブルエクステンションをロードしてインスタンス化することから始まります。そして、それぞれの拡張子で、初期化イベントのオブザーバメソッドがあればそれを検索して登録します。

その後、次のステップを実行します。

  1. スキャン処理が始まる前に

    BeforeBeanDiscovery

    イベントを発生させます.

  2. アーカイブBeansをスキャンするタイプ検出を実行します.

検出された各型が

ProcessAnnotatedType

イベントを発生させます。


AfterTypeDiscovery

イベントを発生させます

  1. Beanディスカバリーを実行します


  2. __AfterBeanDiscovery

    __eventを発生させます

  3. Beanの検証を実行し、定義エラーを検出します


  4. AfterDeploymentValidation

    イベントを発生させます

CDIポータブルエクステンションの目的は、これらのイベントを監視し、検出されたBeanに関するメタデータを確認し、このメタデータを変更するか、または追加することです。

  • CDIポータブルエクステンションでは、これらのイベントのみを観察できます。


6. CDI Portable Extensionを書く

独自のCDIポータブルエクステンションを構築することで、これらのイベントのいくつかにどのように対処できるかを見てみましょう。

6.1. SPIプロバイダの実装

CDIポータブルエクステンションは、インタフェース

javax.enterprise.inject.spi.Extension.

のJava SPIプロバイダです。JavaSPIの概要については、https://www.baeldung.com/java-spi[この記事]をご覧ください。

まず、

Extension

実装を提供することから始めます。後で、CDIコンテナのブートストラップイベントにオブザーバメソッドを追加します。

public class FlywayExtension implements Extension {
}

次に、このコンテンツを含むファイル名

META-INF/services/javax.enterprise.inject.spi.Extension

を追加します。

com.baeldung.cdi.extension.FlywayExtension

SPIとして、この

Extension

はコンテナのブートストラップの前にロードされます。そのため、CDIブートストラップイベントのオブザーバメソッドを登録できます。


6.2. 初期化イベントのオブザーバメソッドの定義

この例では、スキャン処理が始まる前に、

Flyway

クラスをCDIコンテナに認識させます。これは、

registerFlywayType()

observerメソッドで行われます。

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

ここでは、

Flyway

クラスに関するメタデータを追加しました。 ** これ以降は、コンテナによってスキャンされたかのように動作します。

次に、

ProcessAnnotatedType

イベントを観察して、

Flyway

クラスをCDI管理対象Beanにします。

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

まず、

Flyway

クラスに

@ ApplicationScoped

アノテーションと

@ FlywayType

アノテーションを付けてから、

Flyway.setDataSource(DataSource dataSource)

メソッドを検索し、それを

@Inject.

と付けています

上記の操作の最終結果は、コンテナが次の

Flyway

Beanをスキャンした場合と同じ効果があります。

@ApplicationScoped
@FlywayType
public class Flyway {

   //...
    @Inject
    public void setDataSource(DataSource dataSource) {
     //...
    }
}

次のステップは、

Flyway

Beanが

DataSource

Beanに依存しているため、

DataSource

Beanを注入可能にすることです。

そのためには、

DataSource

Beanをコンテナに登録する処理を行い、

AfterBeanDiscovery

イベントを使用します。

void afterBeanDiscovery(@Observes AfterBeanDiscovery abdEvent, BeanManager bm) {
    abdEvent.addBean()
      .types(javax.sql.DataSource.class, DataSource.class)
      .qualifiers(new AnnotationLiteral<Default>() {}, new AnnotationLiteral<Any>() {})
      .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 instance;
      });
}

ご覧のとおり、DataSourceプロパティを提供する

DataSourceDefinition

が必要です。

以下のアノテーションでマネージドBeanにアノテーションを付けることができます。

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

これらのプロパティを抽出するために、

@ WithAnnotations

アノテーションと共に

ProcessAnnotatedType

イベントを観察します。

public void detectDataSourceDefinition(
  @Observes @WithAnnotations(DataSourceDefinition.class) ProcessAnnotatedType<?> patEvent) {
    AnnotatedType at = patEvent.getAnnotatedType();
    dataSourceDefinition = at.getAnnotation(DataSourceDefinition.class);
}

そして最後に、

AfterDeployementValidation

イベントをリスンしてCDIコンテナから必要な

Flyway

Beanを取得してから

migrate()

メソッドを呼び出します。

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

7.まとめ

CDIポータブルエクステンションを作成するのは最初は難しいようですが、コンテナー初期化ライフサイクルとエクステンション専用のSPIを理解すれば、Java EE上でフレームワークを構築するために使用できる非常に強力なツールになります。

いつものように、この記事に示されているすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/flyway-cdi-extension[GitHubで利用可能]です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です