The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

アプリケーションの初期化と終了

アプリケーションの起動時にカスタムアクションを実行し、アプリケーションの停止時にすべてをクリーンアップする必要があることがよくあります。このガイドでは、以下の方法を説明します:

  • メインメソッドでQuarkusアプリケーションを書く

  • タスクを実行して終了するコマンドモードのアプリケーションを書く

  • アプリケーションの開始時に通知される

  • アプリケーションが停止したときに通知される

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 17+がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.9.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

ソリューション

次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

ソリューションは lifecycle-quickstart ディレクトリ にあります。

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。

コマンドラインインタフェース
quarkus create app org.acme:lifecycle-quickstart \
    --no-code
cd lifecycle-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストールと使用方法の詳細については、 Quarkus CLI ガイドを参照してください。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.10.1:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=lifecycle-quickstart \
    -DnoCode
cd lifecycle-quickstart

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

Windowsユーザーの場合:

  • cmdを使用する場合、(バックスラッシュ \ を使用せず、すべてを同じ行に書かないでください)。

  • Powershellを使用する場合は、 -D パラメータを二重引用符で囲んでください。例: "-DprojectArtifactId=lifecycle-quickstart"

以下が生成されます:

  • Mavenの構造

  • nativejvm の両方のモードに対応した Dockerfile ファイルの例

  • アプリケーション設定ファイル

メインメソッド

デフォルトでは、Quarkusは自動的にメインメソッドを生成し、Quarkusをブートストラップして、シャットダウンが開始されるのを待ちます。独自のメインメソッドを用意しましょう:

package com.acme;

import io.quarkus.runtime.annotations.QuarkusMain;
import io.quarkus.runtime.Quarkus;

@QuarkusMain  (1)
public class Main {

    public static void main(String ... args) {
        System.out.println("Running main method");
        Quarkus.run(args); (2)
    }
}
1 このアノテーションは、設定でオーバーライドされない限り、これをメインのメソッドとして使用するようQuarkusに指示します。
2 これによりQuarkusが起動します。

このメインクラスはQuarkusを起動し、停止するまで実行します。これは自動生成されたメインクラスと変わりませんが、MavenやGradleのコマンドを実行する必要がなく、IDEから直接起動するだけで済むという利点があります。

まだQuarkusが設定されておらず、Quarkusは別のClassLoaderで動作する可能性があるため、このメインメソッドでビジネスロジックを行うことは推奨されていません。起動時にロジックを実行したい場合は、以下のように io.quarkus.runtime.QuarkusApplication を使用してください。

起動時に実際にビジネスロジックを実行したい場合(またはタスクを完了してから終了するアプリケーションを書きたい場合)、runメソッドに io.quarkus.runtime.QuarkusApplication クラスを指定する必要があります。Quarkusが起動すると、アプリケーションの run メソッドが呼び出されます。このメソッドが返されると、Quarkusアプリケーションは終了します。

If you want to perform logic on startup you should call Quarkus.waitForExit(), this method will wait until a shutdown is requested (either from an external signal like when you press Ctrl+C or because a thread has called Quarkus.asyncExit()).

その一例が以下のようなものです:

package com.acme;

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class Main {
    public static void main(String... args) {
        Quarkus.run(MyApp.class, args);
    }

    public static class MyApp implements QuarkusApplication {

        @Override
        public int run(String... args) throws Exception {
            System.out.println("Do startup logic here");
            Quarkus.waitForExit();
            return 0;
        }
    }
}

Quarkus.run はコードがエラーを処理できるようにするバージョンも提供します。例:

package com.acme;

import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class Main {
    public static void main(String... args) {
        Quarkus.run(MyApp.class,
        (exitCode, exception) -> {
            // do whatever
        },
        args);
    }

    public static class MyApp implements QuarkusApplication {

        @Override
        public int run(String... args) throws Exception {
            System.out.println("Do startup logic here");
            Quarkus.waitForExit();
            return 0;
        }
    }
}

コマンドライン引数の注入

コマンドラインで渡された引数を注入することができます:

@Inject
@CommandLineArguments
String[] args;

コマンドライン引数は、プロパティ quarkus.args-D フラグを介してアプリケーションに渡すことができます。

  • Quarkus開発モードの場合:

    コマンドラインインタフェース
    quarkus dev -Dquarkus.args=cmd-args
    Maven
    ./mvnw quarkus:dev -Dquarkus.args=cmd-args
    Gradle
    ./gradlew --console=plain quarkusDev -Dquarkus.args=cmd-args
  • runner jarの場合: java -Dquarkus.args=<cmd-args> -jar target/quarkus-app/quarkus-run.jar

  • ネイティブ実行可能ファイルの場合: ./target/lifecycle-quickstart-1.0-SNAPSHOT-runner -Dquarkus.args=<cmd-args>

スタートアップとシャットダウンのイベントをリッスンする

Create a new class named AppLifecycleBean (or pick another name) in the org.acme.lifecycle package, and copy the following content:

package org.acme.lifecycle;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import org.jboss.logging.Logger;

@ApplicationScoped
public class AppLifecycleBean {

    private static final Logger LOGGER = Logger.getLogger("ListenerBean");

    void onStart(@Observes StartupEvent ev) {               (1)
        LOGGER.info("The application is starting...");
    }

    void onStop(@Observes ShutdownEvent ev) {               (2)
        LOGGER.info("The application is stopping...");
    }

}
1 アプリケーションの起動時に呼び出されるメソッド
2 アプリケーションの終了時に呼び出されるメソッド
また、各再配置の間に 開発モード でイベントが呼び出されます。
メソッドは,注入されたBeanにアクセスできます。詳細は AppLifecycleBean.java クラスを確認してください。

@Initialized(ApplicationScoped.class)@Destroyed(ApplicationScoped.class) との違いは?

JVMモードでは、 StartupEvent が常に @Initialized(ApplicationScoped.class)後に 起動され、 ShutdownEvent@Destroyed(ApplicationScoped.class)前に 起動されることを除けば、実質的な違いはありません。しかし、ネイティブの実行形式のビルドでは、 @Initialized(ApplicationScoped.class)ネイティブのビルドプロセスの一部 として起動され、 StartupEvent はネイティブイメージが実行されるときに起動されます。詳細は、Bootstrapの3つのフェーズとQuarkus Philosophy を参照してください。

CDIアプリケーションでは、アプリケーションコンテキストが初期化されたときに、Qualifier @Initialized(ApplicationScoped.class) のイベントが発生します。詳しくは 仕様をご覧ください。

@Startup を使用して、アプリケーション起動時に CDI Bean を初期化する

@Startup でアノテーションされたクラス、プロデューサメソッド、フィールドで表現されたBeanは、アプリケーションの起動時に初期化されます。

package org.acme.lifecycle;

import io.quarkus.runtime.Startup;
import jakarta.enterprise.context.ApplicationScoped;

@Startup (1)
@ApplicationScoped
public class EagerAppBean {

   private final String name;

   EagerAppBean(NameGenerator generator) { (2)
     this.name = generator.createName();
   }
}
1 @Startup でアノテーションされた各Beanに対して、 StartupEvent の合成オブザーバが生成されます。デフォルトの優先度が使用されます。
2 Beanのコンストラクタは、アプリケーションの起動時に呼び出され、結果として得られるコンテキストインスタンスがアプリケーションのコンテキストに格納されます。
@Dependent Beanは、 @Dependent Beanに宣言されたオブザーバーの行動に従うために、その後すぐに破壊されます。
クラスが @Startup でアノテーションされていて、スコープのアノテーションがない場合は、 @ApplicationScoped が自動的に追加されます。

@Startup アノテーションは、非static、非producer、argsなしメソッドでも宣言できます:

package org.acme.lifecycle;

import io.quarkus.runtime.Startup;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class EagerAppBean {

   @Startup
   void init() { (1)
     doSomeCoolInit();
   }
}
1 アプリケーションが起動すると、Bean が作成され、コンテキストインスタンスに対して init() メソッドが呼び出されます。

@Shutdown を使用して、アプリケーションのシャットダウン中に CDI Bean のビジネスメソッドを実行します。

@io.quarkus.runtime.Shutdown アノテーションは、アプリケーションのシャットダウン中に実行されるべき CDI Bean のビジネスメソッドをマークするために使用されます。アノテーションされたメソッドは、非プライベートかつ非静的で、引数を宣言しない必要があります。動作は、 ShutdownEvent オブザーバの宣言に似ています。以下の例は、機能的に同等です。

import io.quarkus.runtime.Shutdown;
import io.quarkus.runtime.ShutdownEvent;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
class Bean1 {
   void onShutdown(@Observes ShutdownEvent event) {
      // place the logic here
   }
}

@ApplicationScoped
class Bean2 {

   @Shutdown
   void shutdown() {
      // place the logic here
   }
}

アプリケーションをパッケージ化して実行する

アプリケーションを次のように実行します:

コマンドラインインタフェース
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

ログメッセージが表示されます。アプリケーションが停止すると、2つ目のログメッセージが印刷されます。

いつものように、アプリケーションは以下の方法でパッケージ化できます:

コマンドラインインタフェース
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

そして次のように実行します java -jar target/quarkus-app/quarkus-run.jar

次のようにネイティブ実行可能ファイルを生成することもできます:

コマンドラインインタフェース
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

起動モード

Quarkusには3種類の起動モードがあります。 NORMAL (つまりプロダクション)、 DEVELOPMENTTEST です。 quarkus:dev を実行している場合は DEVELOPMENT 、JUnit テストを実行している場合は TEST 、それ以外の場合は NORMAL となります。

Your application can get the launch mode by injecting the io.quarkus.runtime.LaunchMode enum into a CDI bean, or by invoking the static method io.quarkus.runtime.LaunchMode.current().

グレースフルシャットダウン

Quarkusがグレースフルシャットダウンのサポートを追加しました。これにより、Quarkusは実行中のリクエストが終了するまで、設定されたタイムアウトまで待機することができます。 デフォルトでは無効になっていますが、 quarkus.shutdown.timeout コンフィグプロパティを設定することで設定できます。 これを設定すると、実行中のリクエストがすべて完了するか、タイムアウトが経過するまでシャットダウンは行われません。

リクエストを受け付けるエクステンションは、個別にサポートを追加する必要があります。 現時点では HTTP エクステンションだけがこれをサポートしているので、メッセージングリクエストがアクティブなときにシャットダウンが発生する可能性があります。

Quarkusは遅延時間をサポートしており、アプリケーションインスタンスは依然としてリクエストに応答しますが、readinessプローブは失敗します。 これにより、インフラストラクチャはインスタンスがシャットダウンしていることを認識し、インスタンスへのトラフィックのルーティングを停止する時間が得られます。 この機能は、ビルド時プロパティ quarkus.shutdown.delay-enabledtrue に設定することで有効にできます。 遅延は、実行時プロパティ quarkus.shutdown.delay を設定することで設定できます。 デフォルトでは設定されていないため、遅延は適用されません。

期間の値を書くには、標準の java.time.Duration フォーマットを使います。 詳細は Duration#parse() javadoc を参照してください。

数字で始まる簡略化した書式を使うこともできます:

  • 数値のみの場合は、秒単位の時間を表します。

  • 数値の後に ms が続く場合は、ミリ秒単位の時間を表します。

その他の場合は、簡略化されたフォーマットが解析のために java.time.Duration フォーマットに変換されます:

  • 数値の後に hms が続く場合は、その前に PT が付けられます。

  • 数値の後に d が続く場合は、その前に P が付けられます。

関連コンテンツ