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

Quarkus对SpringSpring Scheduling API 的扩展

虽然我们鼓励用户使用 常规 Quarkus调度器 ,但Quarkus以 spring-scheduled 扩展的形式为Spring Scheduled提供了一个兼容层。

该指南解释了Quarkus应用程序如何利用众所周知的Spring Scheduled注解来配置和安排任务。

先决条件

要完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • 安装JDK 11以上版本并正确配置了 JAVA_HOME

  • Apache Maven 3.8.1+

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

  • 对Spring Web扩展有一定的熟悉程度

解决方案

我们建议你按照下面几节的说明,一步一步地创建应用程序。然而,你可以直接转到已完成的示例。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或者下载一个 存档

该解决方案位于 spring-scheduled-quickstart 目录中。

创建Maven项目

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

CLI
quarkus create app org.acme:spring-scheduler-quickstart \
    --extension=resteasy-reactive,spring-scheduled \
    --no-code
cd spring-scheduler-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

关于如何安装并使用Quarkus CLI的更多信息,请参考Quarkus CLI指南

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.2.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=spring-scheduler-quickstart \
    -Dextensions="resteasy-reactive,spring-scheduled" \
    -DnoCode
cd spring-scheduler-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

该命令生成了一个包含 spring-scheduled 扩展项的Maven项目。

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

CLI
quarkus extension add 'spring-scheduled'
Maven
./mvnw quarkus:add-extension -Dextensions="spring-scheduled"
Gradle
./gradlew addExtension --extensions="spring-scheduled"

这将在你的构建文件中添加以下内容:

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

创建一个计划作业

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

package org.acme.spring.scheduler;

import org.springframework.scheduling.annotation.Scheduled;

import java.util.concurrent.atomic.AtomicInteger;
import javax.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 scope声明该Bean。Spring只检测Bean上的@Scheduled注解。
2 get() 方法允许检索当前值。
3 使用一个带有类似于cron表达式的Spring @Scheduled 注解来指示Quarkus安排这个方法的运行。在这个例子里,我们将会安排一个每天上午10:15都会被执行的任务。
4 代码非常简单明了。每天上午10点15分,计数器递增。
5 application.properties 中使用类似于cron表达式的方式定义一个名为 cron.expr 的作业。
6 定义一个以固定时间间隔执行的方法。周期以毫秒表示。
7 application.properties 中定义一个以固定时间间隔执行的 fixedRate.expr 作业。

更新该应用程序的配置文件

编辑 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

创建资源和测试

创建包含以下内容的 CountResource 类:

package org.acme.spring.scheduler;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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 注入 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

打包并运行该应用程序

使用以下方式运行该应用程序:

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 clean package
Gradle
./gradlew build

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

你也可以使用以下方式生成本地可执行文件:

CLI
quarkus build --native
Maven
./mvnw package -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

使用属性表达式

Quarkus支持在 application.properties 文件中使用属性表达式,因此为了使任务的配置外部化,你应该在 application.properties 文件中,分别使用 fixedRateString , initialDelayString 参数存储属性。

注意,这个配置是构建时的配置,属性表达将在构建时被解析。

不支持的Spring Scheduled功能

Quarkus目前只支持Spring @Scheduled所提供功能的一个子集,更多的功能正在计划当中。目前暂不支持 fixedDelayfixedDelayString 参数,换句话说, @Scheduled 方法总是被独立地执行。

重要技术说明

Please note that the Spring support in Quarkus does not start a Spring Application Context nor are any Spring infrastructure classes run. Spring classes and annotations are only used for reading metadata and / or are used as user code method return types or parameter types. What that means for end users, is that adding arbitrary Spring libraries will not have any effect. Moreover, Spring infrastructure classes (like org.springframework.beans.factory.config.BeanPostProcessor for example) will not be executed.