アプリケーションの初期化と終了
アプリケーションの起動時にカスタムアクションを実行し、アプリケーションの停止時にすべてをクリーンアップする必要があることがよくあります。このガイドでは、以下の方法を説明します:
-
メインメソッドでQuarkusアプリケーションを書く
-
タスクを実行して終了するコマンドモードのアプリケーションを書く
-
アプリケーションの開始時に通知される
-
アプリケーションが停止したときに通知される
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは lifecycle-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=lifecycle-quickstart"
以下が生成されます:
-
Mavenの構造
-
native
とjvm
の両方のモードに対応した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;
}
}
}
|
コマンドライン引数の注入
コマンドラインで渡された引数を注入することができます:
@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 を参照してください。
In CDI applications, an event with qualifier @Initialized(ApplicationScoped.class) is fired when the application context is initialized. See the spec for more info.
|
@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
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
ログメッセージが表示されます。アプリケーションが停止すると、2つ目のログメッセージが印刷されます。
いつものように、アプリケーションは以下の方法でパッケージ化できます:
quarkus build
./mvnw install
./gradlew build
そして次のように実行します java -jar target/quarkus-app/quarkus-run.jar
。
次のようにネイティブ実行可能ファイルを生成することもできます:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
起動モード
Quarkusには3種類の起動モードがあります。 NORMAL
(つまりプロダクション)、 DEVELOPMENT
、 TEST
です。 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-enabled
を true
に設定することで有効にできます。
遅延は、実行時プロパティ quarkus.shutdown.delay
を設定することで設定できます。
デフォルトでは設定されていないため、遅延は適用されません。
期間の値を書くには、標準の 数字で始まる簡略化した書式を使うこともできます:
その他の場合は、簡略化されたフォーマットが解析のために
|