Application Initialization and Termination
您通常需要在应用程序启动时执行自定义操作,并在应用程序停止时清除所有内容。本指南解释了如何:
You often need to execute custom actions when the application starts and clean up everything when the application stops. This guide explains how to:
-
Write a Quarkus application with a main method
-
Write command mode applications that run a task and then terminate
-
Be notified when the application starts
-
Be notified when the application stops
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
解决方案位于 lifecycle-quickstart
directory 中。
The solution is located in the lifecycle-quickstart
directory.
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
First, we need a new project. Create a new project with the following command:
Unresolved directive in lifecycle.adoc - include::{includes}/devtools/create-app.adoc[]
它生成:
It generates:
-
the Maven structure
-
example
Dockerfile
files for bothnative
andjvm
modes -
the application configuration file
The main method
默认情况下,Quarkus 将自动生成一个主函数,它将引导 Quarkus,然后等待启动时关闭。让我们提供我们自己的主函数:
By default, Quarkus will automatically generate a main method, that will bootstrap Quarkus and then just wait for shutdown to be initiated. Let’s provide our own main method:
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 | This annotation tells Quarkus to use this as the main method, unless it is overridden in the config |
2 | This launches Quarkus |
此主类将引导 Quarkus 并运行它,直到它停止。这与自动生成的主类没有区别,但优点是您可以直接从 IDE 启动它,而无需运行 Maven 或 Gradle 命令。
This main class will bootstrap Quarkus and run it until it stops. This is no different to the automatically generated main class, but has the advantage that you can just launch it directly from the IDE without needing to run a Maven or Gradle command.
不建议在此主函数中执行任何业务逻辑,因为 Quarkus 尚未设置,并且 Quarkus 可能会在不同的 ClassLoader 中运行。如果您想在启动时执行逻辑,请使用如下所述的 io.quarkus.runtime.QuarkusApplication
。
It is not recommenced to do any business logic in this main method, as Quarkus has not been set up yet, and Quarkus may run in a different ClassLoader.
If you want to perform logic on startup use an io.quarkus.runtime.QuarkusApplication
as described below.
如果我们想在启动时实际执行业务逻辑(或编写完成任务然后退出的应用程序),我们需要向 run 方法提供一个 io.quarkus.runtime.QuarkusApplication
类。在 Quarkus 启动之后,将调用应用程序的 run
方法。当此方法返回时,Quarkus 应用程序将退出。
If we want to actually perform business logic on startup (or write applications that complete a task and then exit) we need to supply a io.quarkus.runtime.QuarkusApplication
class to the run method.
After Quarkus has been started the run
method of the application will be invoked.
When this method returns the Quarkus application will exit.
如果要在启动时执行逻辑,则应该调用 Quarkus.waitForExit()
,此方法将一直等到请求关闭(来自外部信号,例如在按下 Ctrl+C
时或因为某个线程调用了 Quarkus.asyncExit()
时)。
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()
).
下面是一个类似示例:
An example of what this looks like is below:
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;
}
}
}
|
Injecting the command line arguments
可以注入命令行中传递的参数:
It is possible to inject the arguments that were passed in on the command line:
@Inject
@CommandLineArguments
String[] args;
可以通过具有 quarkus.args
属性的 -D
标志将命令行参数传递给应用程序:
Command line arguments can be passed to the application through the -D
flag with the property quarkus.args
:
-
For Quarkus dev mode:include::{includes}/devtools/dev-parameters.adoc[]
-
For a runner jar:
java -Dquarkus.args=<cmd-args> -jar target/quarkus-app/quarkus-run.jar
-
For a native executable:
./target/lifecycle-quickstart-1.0-SNAPSHOT-runner -Dquarkus.args=<cmd-args>
Listening for startup and shutdown events
在 org.acme.lifecycle
包中创建一个名为 AppLifecycleBean
的新类(或选择另一个名称),并复制以下内容:
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 | Method called when the application is starting |
2 | Method called when the application is terminating |
重新部署的每个事件中也会调用 dev mode 事件。 |
The events are also called in dev mode between each redeployment. |
这些方法可以访问注入的 Bean。有关详细信息,请查看 AppLifecycleBean.java 类。 |
The methods can access injected beans. Check the AppLifecycleBean.java class for details. |
What is the difference from @Initialized(ApplicationScoped.class)
and @Destroyed(ApplicationScoped.class)
在 JVM 模式下,除了始终通过 after @Initialized(ApplicationScoped.class)
事件触发 StartupEvent
事件,而 ShutdownEvent
事件通过 before @Destroyed(ApplicationScoped.class)
事件触发外,没有真正的区别。但是,对于一个本地可执行版本,@Initialized(ApplicationScoped.class)
事件作为 part of the native build process 事件触发,而 StartupEvent
事件在执行本地镜像时触发。有关更多详细信息,请参阅 Three Phases of Bootstrap and Quarkus Philosophy。
In the JVM mode, there is no real difference, except that StartupEvent
is always fired after @Initialized(ApplicationScoped.class)
and ShutdownEvent
is fired before @Destroyed(ApplicationScoped.class)
.
For a native executable build, however, @Initialized(ApplicationScoped.class)
is fired as part of the native build process, whereas StartupEvent
is fired when the native image is executed.
See Three Phases of Bootstrap and Quarkus Philosophy for more details.
在 CDI 应用程序中,当应用程序上下文初始化后,会触发带限定符 |
In CDI applications, an event with qualifier |
Using @Startup
to initialize a CDI bean at application startup
用 @Startup
注释表示的类、创建者方法或字段的 Bean 在应用程序启动时进行初始化:
A bean represented by a class, producer method or field annotated with @Startup
is initialized at application startup:
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 | For each bean annotated with @Startup a synthetic observer of StartupEvent is generated. The default priority is used. |
2 | The bean constructor is called when the application starts and the resulting contextual instance is stored in the application context. |
|
|
如果类用 |
If a class is annotated with |
@Startup
注释也可以声明在一个非静态非创建者无参数方法上:
The @Startup
annotation can be also declared on a non-static non-producer no-args method:
package org.acme.lifecycle;
import io.quarkus.runtime.Startup;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class EagerAppBean {
@Startup
void init() { 1
doSomeCoolInit();
}
}
1 | The bean is created and the init() method is invoked upon the contextual instance when the application starts. |
Using @Shutdown
to execute a business method of a CDI bean during application shutdown
@io.quarkus.runtime.Shutdown
注释用于标记一个 CDI Bean 的业务方法,该方法应该在应用程序关闭期间执行。带注释的方法必须为非私有且非静态,且不声明参数。此行为与声明一个 ShutdownEvent
观察者类似。以下示例在功能上是等效的。
The @io.quarkus.runtime.Shutdown
annotation is used to mark a business method of a CDI bean that should be executed during application shutdown.
The annotated method must be non-private and non-static and declare no arguments.
The behavior is similar to a declaration of a ShutdownEvent
observer.
The following examples are functionally equivalent.
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
}
}
Package and run the application
使用以下内容运行应用程序:
Run the application with:
Unresolved directive in lifecycle.adoc - include::{includes}/devtools/dev.adoc[]
会打印日志消息。当应用程序停止时,会打印第二个日志消息。
The logged message is printed. When the application is stopped, the second log message is printed.
和往常一样,可以使用以下命令打包应用程序:
As usual, the application can be packaged using:
Unresolved directive in lifecycle.adoc - include::{includes}/devtools/build.adoc[]
并使用 java -jar target/quarkus-app/quarkus-run.jar
执行。
and executed using java -jar target/quarkus-app/quarkus-run.jar
.
您也可以使用以下操作生成本机可执行文件:
You can also generate the native executable using:
Unresolved directive in lifecycle.adoc - include::{includes}/devtools/build-native.adoc[]
Launch Modes
Quarkus 有 3 种不同的启动模式,NORMAL
(即生产)、DEVELOPMENT
和 TEST
。如果您正在运行 quarkus:dev
,则模式将为 DEVELOPMENT
;如果您正在运行 JUnit 测试,则模式将为 TEST
;否则将为 NORMAL
。
Quarkus has 3 different launch modes, NORMAL
(i.e. production), DEVELOPMENT
and TEST
.
If you are running quarkus:dev
then the mode will be DEVELOPMENT
, if you are running a JUnit test it will be TEST
, otherwise it will be NORMAL
.
您的应用程序可以通过将 io.quarkus.runtime.LaunchMode
枚举注入到 CDI Bean 中,或通过调用静态方法 io.quarkus.runtime.LaunchMode.current()
,来获取启动模式。
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()
.
Graceful Shutdown
Quarkus 包括对平稳关机的支持,这允许 Quarkus 等待运行请求完成,直到设定超时。默认情况下,此功能处于禁用状态,但是,您可以通过设置 quarkus.shutdown.timeout
配置属性来配置此功能。当设置此属性后,关机不会在以下时间发生:所有运行的请求都已完成或此超时已过时。
Quarkus includes support for graceful shutdown, this allows Quarkus to wait for running requests to finish, up till a set timeout.
By default, this is disabled, however you can configure this by setting the quarkus.shutdown.timeout
config property.
When this is set shutdown will not happen until all running requests have completed, or until this timeout has elapsed.
接受请求的扩展需要一个个添加对此功能的支持。现在,仅 HTTP 扩展支持此功能,因此当消息请求处于活动状态时仍可能发生关机。
Extensions that accept requests need to add support for this on an individual basis. At the moment only the HTTP extension supports this, so shutdown may still happen when messaging requests are active.
Quarkus 支持延迟时间,在此延迟期间应用程序实例仍对请求做出响应,但就绪探查失败。这可以给基础设施时间来识别实例正在关闭并停止向实例路由流量。通过将编译时属性 quarkus.shutdown.delay-enabled
设置为 true
,可以启用此功能。然后,可以通过设置运行时属性 quarkus.shutdown.delay
来配置延迟。默认情况下未设置此属性,因此不应用延迟。
Quarkus supports a delay time, where the application instance still responds to requests, but the readiness probe fails.
This gives the infrastructure time to recognize that the instance is shutting down and stop routing traffic to the instance.
This feature can be enabled by setting the build-time property quarkus.shutdown.delay-enabled
to true
.
The delay can then be configured by setting the runtime property quarkus.shutdown.delay
.
It is not set by default, thus no delay is applied.
Unresolved directive in lifecycle.adoc - include::{includes}/duration-format-note.adoc[]