Quarkus Extension for Spring Scheduling API

尽管 Quarkus 鼓励用户使用 regular Quarkus scheduler,但 Quarkus 提供了针对采用 spring-scheduled 扩展形式的 Spring Scheduled 的兼容层。 本指南说明 Quarkus 应用程序如何利用众所周知的 Spring Scheduled 注解,以配置和计划任务。

Prerequisites

include::./_includes/prerequisites.adoc[]* 熟悉 Spring Web 扩展

Solution

我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

解决方案位于 spring-scheduled-quickstart directory

Creating the Maven project

首先,我们需要一个新项目。使用以下命令创建一个新项目:

CLI
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
    --no-code
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 --gradle--gradle-kotlin-dsl 选项。 有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。

Maven
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId={create-app-group-id} \
    -DprojectArtifactId={create-app-artifact-id} \
    -DnoCode
cd {create-app-artifact-id}

要创建一个 Gradle 项目,添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

适用于 Windows 用户:

  • 如果使用 cmd,(不要使用反斜杠 \ ,并将所有内容放在同一行上)

  • 如果使用 Powershell,将 -D 参数用双引号引起来,例如 "-DprojectArtifactId={create-app-artifact-id}"

此命令生成一个带有 spring-scheduled 扩展的 Maven 项目。

如果已经配置好你的 Quarkus 项目,则可以通过在项目基础目录中运行以下命令,将 spring-scheduled 扩展添加到你的项目中:

CLI
quarkus extension add {add-extension-extensions}
Maven
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
Gradle
./gradlew addExtension --extensions='{add-extension-extensions}'

这会将以下内容添加到构建文件中:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-scheduled</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-scheduled")

Creating a scheduled job

org.acme.spring.scheduler 包中,使用以下内容创建 CounterBean 类:

package org.acme.spring.scheduler;

import org.springframework.scheduling.annotation.Scheduled;

import java.util.concurrent.atomic.AtomicInteger;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped              (1)
public class CounterBean {

    private AtomicInteger counter = new AtomicInteger();

    public int get() {   (2)
        return counter.get();
    }

    @Scheduled(cron="*/5 * * * * ?")   (3)
    void cronJob() {
        counter.incrementAndGet();      (4)
        System.out.println("Cron expression hardcoded");
    }

    @Scheduled(cron = "{cron.expr}")   (5)
    void cronJobWithExpressionInConfig() {
        counter.incrementAndGet();
        System.out.println("Cron expression configured in application.properties");
    }

    @Scheduled(fixedRate = 1000)    (6)
    void jobAtFixedRate() {
        counter.incrementAndGet();
        System.out.println("Fixed Rate expression");
    }

    @Scheduled(fixedRateString = "${fixedRate.expr}")      (7)
    void jobAtFixedRateInConfig() {
        counter.incrementAndGet();
        System.out.println("Fixed Rate expression configured in application.properties");
    }
}
1 application 范围内声明 Bean。Spring 仅在 Bean 中检测 @Scheduled 注解。
2 get() 方法允许获取当前值。
3 使用 Spring @Scheduled 注解并附带类似 cron 的表达式,指示 Quarkus 计划运行此方法。在本示例中,我们计划每天上午 10:15 执行一项任务。
4 代码非常简单。每天上午 10:15,计数器都会递增一次。
5 使用在 application.properties 中可配置的类似 cron 的表达式 cron.expr 定义一项作业。
6 定义一个方法,让其在固定的时间间隔内执行。时段以毫秒为单位表示。
7 使用在 application.properties 中可配置的 fixedRate.expr 定义一项作业,让其在固定的时间间隔内执行。

Updating the application configuration file

编辑 application.properties 文件并添加 cron.exprfixedRate.expr 配置:

# The syntax used by Spring for cron expressions is the same as which is used by regular Quarkus scheduler.
cron.expr=*/5 * * * * ?
fixedRate.expr=1000

Creating the resource and the test

使用以下内容创建 CountResource 类:

package org.acme.spring.scheduler;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/count")
public class CountResource {

    @Inject
    CounterBean counter;    (1)


    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "count: " + counter.get();   (2)
    }
}
1 Inject the CounterBean
2 发回当前计数器值

我们还需要更新测试。编辑 CountResourceTest 类以进行匹配:

package org.acme.spring.scheduler;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class CountResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
                .when().get("/count")
                .then()
                .statusCode(200)
                .body(containsString("count"));  (1)
    }

}
1 确保响应包含 count

Package and run the application

使用以下内容运行应用程序:

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

在另一个终端中,运行 curl localhost:8080/count 以检查计数器值。几秒钟后,重新运行 curl localhost:8080/count 以验证计数器已被增量。

观察控制台以验证已显示以下消息:- Cron expression hardcoded- Cron expression configured in application.properties- Fixed Rate expression- Fixed Rate expression configured in application.properties`这些消息表明使用了 `@Scheduled 注释的方法执行已被触发。

和往常一样,可以使用以下命令打包应用程序:

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build

并使用 `java -jar target/quarkus-app/quarkus-run.jar`执行。

你还可以按如下方式生成本机可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

Using Property Expressions

Quarkus 在 application.properties 文件中支持使用属性表达式,所以要使任务的配置外在化,你应该将属性存储在 application.properties 文件中,并分别使用 fixedRateStringinitialDelayString 参数。

请注意,此配置是构建时配置,属性表达式将在构建时解析。

Unsupported Spring Scheduled functionalities

Quarkus 当前仅支持 Spring @Scheduled 提供的部分功能,并计划更多功能。目前,fixedDelayfixedDelayString 参数不受支持,换句话说,@Scheduled 方法始终独立执行。

Important Technical Note

请注意,Quarkus 中的 Spring 支持不会启动 Spring 应用程序上下文,也不会运行任何 Spring 基础设施类。Spring 类和注释仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。这对最终用户意味着,添加任意 Spring 库不会产生任何影响。此外,Spring 基础设施类(例如 org.springframework.beans.factory.config.BeanPostProcessor)不会被执行。