Spring Boot中Cron表达式实现每2分钟执行任务

2026-09-06 13:58:22 0 次阅读

@Scheduled 是 Spring Boot 中实现定时任务最常用的方式之一。如果业务要求任务每隔 2 分钟自动执行,可以通过 Cron 表达式进行精确配置。相比固定间隔的毫秒数,Cron 表达式更适合表达“每小时第几分钟”“每天固定时间”等具有时间规律的任务。

开启 Spring Boot 定时任务

使用 @Scheduled 之前,需要先开启 Spring 的定时任务能力。通常在启动类或者配置类上添加 @EnableScheduling

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class Application {

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

@EnableScheduling 的作用是让 Spring 容器识别并执行带有 @Scheduled 注解的方法。如果忘记添加这个注解,即使 Cron 表达式配置正确,定时任务也不会自动运行。

每2分钟执行一次的Cron表达式

Spring 的 Cron 表达式通常包含 6 个字段:

秒 分 时 日 月 星期

如果要求每隔 2 分钟执行一次,可以使用:

0 */2 * * * *

各字段含义如下:

字段含义
0每次在第 0 秒触发
*/2每隔 2 分钟
*任意小时
*任意日期
*任意月份
星期*任意星期

因此:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    System.out.println("定时任务执行:" + System.currentTimeMillis());
}

任务会在类似下面的时间点执行:

10:00:00
10:02:00
10:04:00
10:06:00
10:08:00
...

需要特别注意,这里的“每2分钟”并不是从应用启动时间开始计时 2 分钟后执行,而是按照自然时间的分钟刻度执行。例如应用在 10:01:35 启动,下一次触发通常是 10:02:00,之后是 10:04:0010:06:00

完整的Spring Boot定时任务示例

创建一个任务组件:

Java
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
public class DemoScheduledTask {

    @Scheduled(cron = "0 */2 * * * *")
    public void executeTask() {
        System.out.println("任务执行时间:" + LocalDateTime.now());
    }
}

只要项目已经启用 @EnableScheduling,Spring Boot 启动后就会自动发现这个 Bean,并按照 Cron 表达式周期执行 executeTask()

如果希望把任务日志记录下来,实际项目中更推荐使用日志框架:

Java
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class DemoScheduledTask {

    @Scheduled(cron = "0 */2 * * * *")
    public void executeTask() {
        log.info("开始执行定时任务");
        
        // 具体业务逻辑
    }
}

这样比直接使用 System.out.println() 更适合生产环境,也方便后续进行日志检索和问题排查。

*/2为什么表示每2分钟

Cron 中的 */2 可以理解为“以 2 为步长进行匹配”。

对于分钟字段:

*/2

相当于:

0,2,4,6,8,10,12,14,...,58

所以:

0 */2 * * * *

实际上表示“当秒数为 0,并且分钟数属于偶数分钟时执行”。

类似地:

0 */5 * * * *

表示每 5 分钟执行:

00、05、10、15、20、25、30、35、40、45、50、55

而:

0 */10 * * * *

表示每 10 分钟执行一次。

@Scheduled的fixedRate与Cron有什么区别

Spring Boot 定时任务除了 Cron,还提供 fixedRatefixedDelay

例如:

Java
@Scheduled(fixedRate = 120000)
public void executeTask() {
    // 每120000毫秒执行
}

120000 毫秒就是 2 分钟。

这种方式适合单纯按照固定时间间隔执行的任务,而 Cron 更适合具有明确时间规则的场景。

fixedRate 的特点是按照任务开始时间计算下一次执行时间。例如:

10:00 开始
10:02 开始
10:04 开始

fixedDelay 则是在上一次任务执行完成之后,再等待指定时间:

Java
@Scheduled(fixedDelay = 120000)
public void executeTask() {
    // 任务完成后等待2分钟
}

假设任务本身执行了 30 秒:

10:00:00 开始
10:00:30 完成
10:02:30 再次执行

因此,如果需求描述的是“每隔2分钟按照自然时间执行”,Cron 通常更加直观:

Java
@Scheduled(cron = "0 */2 * * * *")

Spring Cron与Linux Cron不要混淆

这是实际开发中比较容易出现的问题。

Linux Crontab 常见的 Cron 格式通常是 5 个字段:

分 时 日 月 星期

例如:

*/2 * * * *

表示每 2 分钟执行一次。

而 Spring 的 @Scheduled(cron = ...) 通常使用 6 个字段:

秒 分 时 日 月 星期

所以 Spring Boot 中应该写:

Java
@Scheduled(cron = "0 */2 * * * *")

而不能直接把 Linux Crontab 中的:

*/2 * * * *

复制到 @Scheduled

两者虽然都叫 Cron,但字段结构并不完全相同。配置定时任务时一定要根据具体执行环境确认表达式格式。

将Cron表达式放到配置文件

如果任务执行周期未来可能发生变化,不建议把 Cron 表达式直接硬编码在 Java 代码中。

可以放到 application.yml

YAML
app:
  task:
    cron: "0 */2 * * * *"

Java 代码通过配置占位符读取:

Java
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class DemoScheduledTask {

    @Scheduled(cron = "${app.task.cron}")
    public void executeTask() {
        System.out.println("执行定时任务");
    }
}

这样需要修改执行周期时,只需要修改配置文件即可,不必重新修改业务代码。

也可以在不同环境中使用不同配置:

YAML
app:
  task:
    cron: "0 */2 * * * *"

生产环境如果需要调整为每 5 分钟执行,可以配置:

YAML
app:
  task:
    cron: "0 */5 * * * *"

这种方式尤其适合测试环境、预发布环境和生产环境存在不同调度策略的项目。

使用zone解决时区问题

如果应用部署在云服务器上,而业务要求按照特定地区的本地时间运行,可以指定 zone

Java
@Scheduled(
    cron = "0 */2 * * * *",
    zone = "Asia/Shanghai"
)
public void executeTask() {
    // 定时任务
}

例如:

Java
@Scheduled(
    cron = "0 */2 * * * *",
    zone = "Asia/Tokyo"
)
public void executeTask() {
    // 任务逻辑
}

是否需要指定 zone,取决于业务对时间的要求。如果只是简单的“每2分钟执行一次”,通常不需要额外配置时区;如果任务涉及账单结算、日报生成、营业时间等场景,则建议明确时区,避免服务器时区与业务时区不一致。

定时任务执行时间较长怎么办

假设每 2 分钟执行一次任务,但任务本身需要 5 分钟才能完成:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    // 可能执行5分钟
}

这时就不能简单认为任务一定会同时启动多个实例。Spring 默认的调度执行机制通常使用单线程调度器,因此任务执行时间过长时,后续任务可能出现延迟。

对于耗时任务,可以结合线程池:

Java
@Configuration
@EnableScheduling
public class SchedulingConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean
    public ScheduledExecutorService taskExecutor() {
        return Executors.newScheduledThreadPool(4);
    }
}

不过,增加线程数并不意味着可以忽略并发问题。如果同一个任务可能重叠执行,就需要考虑数据一致性、重复处理和幂等设计。

多实例部署时的重复执行问题

这是 Spring Boot 定时任务进入生产环境后非常重要的一点。

假设应用部署了 3 个实例:

Server A
Server B
Server C

三个实例都配置:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    // 业务处理
}

到了 10:00:00,三个应用实例都有可能执行这个方法。

如果任务是:

生成一次订单结算数据
发送一次通知
同步一次数据
清理一次业务记录

那么多实例同时执行就可能造成重复处理。

这类场景不能仅靠修改 Cron 表达式解决,而需要考虑分布式任务调度方案,或者使用分布式锁、数据库锁等机制确保同一时间只有一个实例执行任务。

例如项目使用分布式锁时,可以让任务获得锁之后再执行:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    // 尝试获取分布式锁

    // 获取成功后执行任务

    // 最后释放锁
}

实际使用哪一种方案,应根据部署架构、任务重要程度以及数据一致性要求决定。

定时任务中的异常处理

不要让定时任务中的异常处理过于随意。例如:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    doSomething();
}

如果 doSomething() 抛出异常,应该根据业务情况进行日志记录、重试或告警。

一种简单的写法是:

Java
@Scheduled(cron = "0 */2 * * * *")
public void executeTask() {
    try {
        doSomething();
    } catch (Exception e) {
        log.error("每2分钟执行的任务发生异常", e);
    }
}

对于支付、订单、消息同步等关键任务,则不应该仅仅打印异常日志,而应进一步设计失败重试、状态记录和告警机制。

常见Cron表达式对照

掌握几个常见表达式,可以快速完成大部分 Spring Boot 定时任务配置:

0 */2 * * * *       每2分钟执行一次
0 */5 * * * *       每5分钟执行一次
0 */10 * * * *      每10分钟执行一次
0 */30 * * * *      每30分钟执行一次
0 0 * * * *         每小时执行一次
0 0 0 * * *         每天零点执行
0 0 9 * * *         每天上午9点执行
0 0 9 * * MON       每周一上午9点执行

其中最符合“Spring Boot中每2分钟执行一次任务”这一需求的配置就是:

Java
@Scheduled(cron = "0 */2 * * * *")

完整示例可以简化成:

Java
@EnableScheduling
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
Java
@Component
public class TaskService {

    @Scheduled(cron = "0 */2 * * * *")
    public void execute() {
        System.out.println("每2分钟执行一次");
    }
}

只要 @EnableScheduling 已经启用,任务类被 Spring 容器扫描并注册为 Bean,应用启动后即可按照 Cron 规则自动执行。

实际项目中,如果只是简单的周期性任务,0 */2 * * * * 足以满足需求;如果任务耗时较长、需要并发执行,或者应用采用多实例集群部署,则还应进一步考虑线程池、分布式锁、幂等、失败重试和任务监控等问题。这样才能让“每2分钟执行一次”从开发阶段的简单配置,真正变成稳定可靠的生产级定时任务。