ZetCode

Spring Boot 调度任务

最后修改于 2023 年 7 月 16 日

Spring Boot 调度任务教程展示了如何在 Spring Boot 应用程序中使用 @Scheduled 调度任务。

Spring Boot 是一个流行的框架,用于构建 Java、Kotlin 或 Groovy 中的企业应用程序。

Spring Boot 调度任务

@EnableScheduling 在 Spring Boot 应用程序中启用调度。使用 @Scheduled 注解修饰的方法会定期运行。这些方法应该返回 void 并且不应该有任何参数。

ScheduledAnnotationBeanPostProcessor 是一个 bean 后处理器,它注册用 @Scheduled 注解的方法,这些方法将由 TaskScheduler 根据通过注解提供的 fixedRatefixedDelaycron 表达式调用。 fixedDelay 属性以连续执行任务之间 n 毫秒的固定延迟运行任务。 fixedRate 以每 n 毫秒运行一次调度任务。它不会检查任务的任何先前执行。

@Scheduled(cron="pattern") 允许定义 crontab 模式来运行任务。该模式是一个由六个单独的空格分隔的字段组成的列表:分别代表秒、分钟、小时、日、月、星期。月份和星期几的名称可以用英文名称的前三个字母表示。例如,"0 0/30 8-10 * * *" cron 模式将任务安排在每天 8:00、8:30、9:00、9:30、10:00 和 10:30 运行。

Spring Boot 调度示例

在以下应用程序中,我们以 15 秒的固定频率调度一个任务。该任务连接到一个网站并读取其 date 标头。

build.gradle
...
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───scheduling
│   │           │       ScheduledTasks.java
│   │           └───service
│   │                   HeadRequestService.java
│   └───resources
│           application.properties
└───test
    └───java

这是 Spring Boot 应用程序的项目结构。

build.gradle
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.zetcode'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

这是 Gradle 构建文件。我们添加了 spring-boot-starter-web 用于一个简单的 web 应用程序。

resources/application.properties
spring.main.banner-mode=off
spring.main.log-startup-info=false

application.properties 文件包含应用程序配置设置。使用 spring.main.banner-mode,我们关闭了 Spring Boot banner,使用 spring.main.log-startup-info 属性,我们关闭了启动日志信息。

com/zetcode/scheduling/ScheduledTasks.java
package com.zetcode.scheduling;

import com.zetcode.service.HeadRequestService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    private final HeadRequestService headRequestService;

    @Autowired
    public ScheduledTasks(HeadRequestService headRequestService) {
        this.headRequestService = headRequestService;
    }

    @Scheduled(fixedRate = 15000)
    public void getHeadValue() {
        log.info("Value: {}", headRequestService.doHeadRequest());
    }
}

ScheduledTasks 中,我们调度一个任务每 15 秒运行一次。

@Scheduled(fixedRate = 15000)
public void getHeadValue() {
    log.info("Value: {}", headRequestService.doHeadRequest());
}

每 15 秒,HeadRequestServicedoHeadRequest 方法被调用。

com/zetcode/service/HeadRequestService.java
package com.zetcode.service;

import com.zetcode.scheduling.ScheduledTasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

@Service
public class HeadRequestService {

    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    private HttpHeaders headers;

    public String doHeadRequest() {

        HttpClient client = HttpClient.newHttpClient();

        try {
            var request = HttpRequest.newBuilder(URI.create("http://webcode.me"))
                    .method("HEAD", HttpRequest.BodyPublishers.noBody())
                    .build();

            HttpResponse<Void> response = client.send(request,
                    HttpResponse.BodyHandlers.discarding());

            headers = response.headers();

        } catch (IOException | InterruptedException e) {

            log.error("Failed to send HEAD request");
        }

        var opt = headers.firstValue("date");
        return opt.orElse("");
    }
}

doHeadRequest 方法向 webcode.me 网站发出 HEAD 请求,并从其响应中检索 date 标头。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableScheduling
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @GetMapping(value = "/", produces = MediaType.TEXT_PLAIN_VALUE)
    private String home() {

        return "home page";
    }
}

Applicaiton 中,我们设置了 Spring Boot 应用程序。使用 @EnableScheduling,我们为应用程序启用调度。此外,我们添加了一个简单的网页,返回文本。

$ ./gradlew bootRun
...
2023-07-17T18:38:54.662+02:00  INFO 16732 --- [   scheduling-1] com.zetcode.scheduling.ScheduledTasks    : Value: Mon, 17 Jul 2023 16:37:43 GMT
2023-07-17T18:39:09.167+02:00  INFO 16732 --- [   scheduling-1] com.zetcode.scheduling.ScheduledTasks    : Value: Mon, 17 Jul 2023 16:37:58 GMT
2023-07-17T18:39:24.165+02:00  INFO 16732 --- [   scheduling-1] com.zetcode.scheduling.ScheduledTasks    : Value: Mon, 17 Jul 2023 16:38:13 GMT

我们使用 ./gradlew bootRun 运行应用程序。在输出中,我们可以看到已调度方法的消息。

在本文中,我们研究了 Spring Boot 应用程序中的调度。

作者

我的名字是 Jan Bodnar,我是一个充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程