Spring 自带的 @Scheduled (最简单、最常用)
这是 Spring Framework 3.0 之后引入的注解,使用起来非常简单,无需引入额外的依赖,它基于 Java 自带的 java.util.concurrent.ScheduledExecutorService 实现。

核心步骤
第一步:添加 @EnableScheduling 注解
在你的 Spring Boot 应用的主类或者任何 @Configuration 配置类上,添加 @EnableScheduling 注解,用来开启对 @Scheduled 注解的支持。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // 开启定时任务功能
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
第二步:创建任务类并注入 Spring 容器
创建一个普通的 Java 类,并在类上添加 @Component (或 @Service, @Repository 等) 注解,将其交给 Spring 管理。

第三步:在方法上添加 @Scheduled 注解
在类中的任何一个 public 方法上添加 @Scheduled 注解,并设置 cron 表达式或其他属性。
@Scheduled 注解详解
@Scheduled 注解主要有以下几个属性:
cron: 最强大的属性,用于定义复杂的执行时间规则,它接收一个 Cron 表达式。fixedDelay: 表示上一个任务执行完毕后,等待多久再执行下一个任务,单位是毫秒。@Scheduled(fixedDelay = 5000)表示任务执行完后,等待5秒再执行。fixedRate: 表示按照固定的频率执行任务,与上一个任务的开始时间点相关。@Scheduled(fixedRate = 5000)表示每隔5秒执行一次,无论上一次任务是否执行完毕。fixedDelayString/fixedRateString: 与上面两个类似,但值是字符串类型,可以配置在.properties或.yml文件中,实现动态配置。
代码示例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(MyScheduledTasks.class);
/**
* fixedDelay: 上一个任务执行完毕后,等待5秒再执行。
*/
@Scheduled(fixedDelay = 5000)
public void taskWithFixedDelay() {
log.info("执行 fixedDelay 任务,当前时间: {}", System.currentTimeMillis());
try {
// 模拟一个耗时3秒的任务
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* fixedRate: 每隔5秒执行一次,不管上一次任务是否完成。
* 如果任务执行时间超过了间隔时间,任务会重叠执行。
*/
@Scheduled(fixedRate = 5000)
public void taskWithFixedRate() {
log.info("执行 fixedRate 任务,当前时间: {}", System.currentTimeMillis());
}
/**
* cron: 使用 Cron 表达式,每天凌晨1点执行。
*/
@Scheduled(cron = "0 0 1 * * ?")
public void taskWithCron() {
log.info("执行 Cron 任务,每天凌晨1点执行,当前时间: {}", System.currentTimeMillis());
}
}
Cron 表达式详解
Cron 表达式是一个字符串,由6-7个时间字段组成,用来定义任务的执行时间。

| 字段 | 允许的值 | 特殊字符 |
|---|---|---|
| 秒 | 0-59 |
|
| 分 | 0-59 |
|
| 时 | 0-23 |
|
| 日期 | 1-31 |
, - * / ? L W |
| 月份 | 1-12 或 JAN-DEC |
|
| 星期 | 1-7 或 SUN-SAT |
, - * / ? L # |
| 年份 (可选) | 1970-2099 |
特殊字符含义:
- 匹配该字段的所有值。 在“分”字段上表示“每分钟”。
- 只用于“日期”和“星期”字段,表示“不指定值”,这两个字段通常互斥,需要用 来避免冲突。
- 表示一个范围。
10-12在“时”字段上表示“10点到12点”。 - 表示一个列表。
MON, WED, FRI在“星期”字段上表示“周一、周三、周五”。 - 表示一个增量。
0/15在“分”字段上表示“从0分钟开始,每15分钟一次”(即0, 15, 30, 45)。 L: "Last" 的缩写。- 在“日期”字段上,表示“该月的最后一天”。
- 在“星期”字段上,表示“星期六”(7)或“最后指定的星期几”(如
5L表示“最后一个星期五”)。
W: "Weekday" 的缩写,表示“最近的工作日”。15W表示“如果15号是周六,则触发于14号(周五);如果是周日,则触发于16号(周一)”。- 表示“该月第几个星期几”。
6#3表示“该月第三个星期五”。
常用 Cron 表达式示例:
| 表达式 | 说明 |
|---|---|
0/5 * * * * ? |
每5秒执行一次 |
0 0/10 * * * ? |
每10分钟执行一次 |
0 0 0/1 * * ? |
每小时执行一次 |
0 0 8 * * ? |
每天8点执行 |
0 0 12 * * ? |
每天12点(中午)执行 |
0 0 18 * * ? |
每天18点(下午6点)执行 |
0 0 22 * * ? |
每天22点(晚上10点)执行 |
0 0 0 1 * ? |
每月1号凌晨0点执行 |
0 15 10 ? * MON-FRI |
每周一至周五的上午10:15执行 |
0 15 10 L * ? |
每月最后一天的上午10:15执行 |
0 15 10 ? * 6L |
每月最后一个星期五的上午10:15执行 |
0 15 10 * * ? 2025 |
在2025年,每天的上午10:15执行 |
集成 Quartz (功能更强大、支持集群)
Quartz 是一个功能非常成熟和强大的开源作业调度库,当 @Scheduled 无法满足需求时(需要持久化任务、支持动态增删改任务、构建集群环境等),Quartz 是更好的选择。
核心概念
- Job (作业): 定义需要执行的任务逻辑,是一个接口,只需实现
execute()方法。 - JobDetail (作业详情): 包含 Job 的实例以及调度所需的各种信息。
- Trigger (触发器): 定义 Job 的执行时间规则,主要有两种:
SimpleTrigger: 简单触发器,用于在指定时间执行一次或重复执行多次。CronTrigger: Cron 触发器,功能与@Scheduled的 cron 类似,但更强大。
- Scheduler (调度器): 核心调度容器,负责将 Job 和 Trigger 绑定在一起并执行。
集成步骤
第一步:添加依赖
在 pom.xml 中添加 Spring Boot Starter for Quartz 的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
第二步:创建 Job 类
创建一个类实现 org.quartz.Job 接口。
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyQuartzJob implements Job {
private static final Logger log = LoggerFactory.getLogger(MyQuartzJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("执行 Quartz Job,当前时间: {}", System.currentTimeMillis());
// 在这里编写你的业务逻辑
}
}
第三步:配置 Job 和 Trigger
在 Spring Boot 的配置类中,通过 @Bean 的方式来定义 JobDetail 和 Trigger,并将它们交给 Scheduler。
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
/**
* 1. 定义 JobDetail
*/
@Bean
public JobDetail myJobDetail() {
// JobDetail 的绑定名是 "myJobDetail"
// 不持久化,每次调度都会创建一个新的 Job 实例
return JobBuilder.newJob(MyQuartzJob.class)
.withIdentity("myJobDetail") // 给 JobDetail 一个唯一的标识
.storeDurably() // 即使没有 Trigger 关联,也保留 JobDetail
.build();
}
/**
* 2. 定义 Trigger
*/
@Bean
public Trigger myJobTrigger() {
// 简单触发器:立即执行,然后每隔5秒重复一次
// SimpleScheduleBuilder simpleSchedule = SimpleScheduleBuilder.simpleSchedule()
// .withIntervalInSeconds(5)
// .repeatForever();
// Cron 触发器:每10秒执行一次
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(myJobDetail()) // 关联上面定义的 JobDetail
.withIdentity("myJobTrigger") // 给 Trigger 一个唯一的标识
.withSchedule(cronSchedule) // 设置调度规则
.build();
}
}
Quartz 优势:
- 持久化: 可以将 Job 和 Trigger 信息保存到数据库中,即使应用重启,任务也不会丢失。
- 集群: 支持多节点集群部署,通过数据库锁机制保证任务不会重复执行。
- 动态管理: 可以在运行时通过 API 动态地添加、删除、修改和暂停任务。
Elastic-Job (分布式任务调度)
如果你的应用是微服务架构,并且需要在多台服务器上协同执行定时任务(每台服务器只执行自己负责的任务,或者多台服务器共同完成一个大任务),Elastic-Job 是一个非常好的选择。
它由当当网开源,并已成为 Apache ShardingSphere 项目的一部分,是专门为分布式环境设计的任务调度解决方案。
核心特性
- 分布式: 无中心化,协调通过 ZK 或 Etcd 完成。
- 高可用: 任务在多个节点上运行,单个节点宕机不影响整体任务。
- 分片: 将一个任务拆分成多个分片,每个节点执行不同的分片,实现任务并行处理。
- 失效转移: 节点宕机后,其任务会自动转移到其他存活的节点上执行。
- 限流: 支持设置每个分片的并发线程数,防止任务执行过载。
集成步骤 (以 Spring Boot + ZK 为例)
第一步:添加依赖
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring-boot-starter</artifactId>
<version>3.0.1</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.1</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.4.0</version> <!-- 使用最新版本 -->
</dependency>
第二步:配置 ZK 连接
在 application.yml 中配置 ZK 的连接信息。
elasticjob:
reg-center:
server-lists: localhost:2181
namespace: elastic-job-demo
dataflow:
jdbc:
datasource:
url: jdbc:mysql://localhost:3306/elastic_job?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: password
第三步:创建并配置 Job
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.dataflow.DataflowJob;
import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.JobTypeConfiguration;
import com.dangdang.ddframe.job.lite.api.JobScheduler;
import com.dangdang.ddframe.job.lite.api.bootstrap.JobBootstrap;
import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration;
import com.dangdang.ddframe.job.lite.spring.api.SpringJobScheduler;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import com.dangdang.ddframe.job.util.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
@Configuration
public class ElasticJobConfig {
@Autowired
private ZookeeperRegistryCenter regCenter;
@Autowired
private DataSource dataSource;
/**
* 定义一个简单的定时任务
*/
@Bean
public SimpleJob mySimpleJob() {
return new MySimpleJob();
}
@Bean
public JobScheduler mySimpleJobScheduler(final SimpleJob simpleJob) {
return new SpringJobScheduler(simpleJob, regCenter, getLiteJobConfiguration("mySimpleJob", "0/5 * * * * ?", 3, simpleJob.getClass()));
}
/**
* 定义一个数据处理流任务
*/
@Bean
public DataflowJob<MyDataflowJob> myDataflowJob() {
return new MyDataflowJob();
}
@Bean
public JobScheduler myDataflowJobScheduler(final DataflowJob<MyDataflowJob> dataflowJob) {
return new SpringJobScheduler(dataflowJob, regCenter, getLiteJobConfiguration("myDataflowJob", "0/10 * * * * ?", 3, dataflowJob.getClass()));
}
private LiteJobConfiguration getLiteJobConfiguration(String jobName, String cron, int shardingTotalCount, Class<? extends com.dangdang.ddframe.job.api.Job> jobClass) {
JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder(jobName, cron, shardingTotalCount)
.shardingItemParameters("0=Beijing,1=Shanghai,2=Guangzhou") // 可选:为每个分片指定参数
.build();
JobTypeConfiguration typeConfig = new JobTypeConfiguration(jobClass);
return LiteJobConfiguration.newBuilder(coreConfig, typeConfig)
.overwrite(true) // 允许覆盖配置
.build();
}
}
// 简单任务实现
class MySimpleJob implements com.dangdang.ddframe.job.api.Job {
@Override
public void execute(ShardingContext shardingContext) {
System.out.println("分片项: " + shardingContext.getShardingItem() + " 正在执行任务,当前时间: " + System.currentTimeMillis());
// 你的业务逻辑
}
}
// 数据处理流任务实现
class MyDataflowJob implements DataflowJob<String> {
@Override
public List<String> fetchData(ShardingContext shardingContext) {
// 从数据源(如数据库)获取一批待处理数据
System.out.println("分片项: " + shardingContext.getShardingItem() + " 正在获取数据...");
// return fetchDataFromDB(shardingContext.getShardingItem());
return null; // 示例中返回空
}
@Override
public void processData(ShardingContext shardingContext, List<String> data) {
// 处理获取到的数据
System.out.println("分片项: " + shardingContext.getShardingItem() + " 正在处理数据: " + data);
// processBatchData(data);
}
}
总结与如何选择
| 特性 | Spring @Scheduled |
Quartz | Elastic-Job |
|---|---|---|---|
| 易用性 | ⭐⭐⭐⭐⭐ (非常简单) | ⭐⭐⭐ (需要配置) | ⭐⭐ (相对复杂) |
| 功能丰富度 | ⭐⭐ (基础) | ⭐⭐⭐⭐⭐ (非常强大) | ⭐⭐⭐⭐ (专注分布式) |
| 持久化 | ❌ (不支持) | ✅ (支持) | ✅ (支持) |
| 集群 | ❌ (不支持) | ✅ (支持) | ✅ (原生支持,强项) |
| 动态管理 | ❌ (不支持) | ✅ (支持) | ✅ (支持) |
| 分片 | ❌ (不支持) | ❌ (不支持) | ✅ (核心功能) |
| 适用场景 | 单机应用,简单的定时任务 | 中大型应用,需要复杂调度和持久化的场景 | 分布式/微服务架构下的任务调度 |
选择建议:
-
如果你的应用是单体应用,定时任务需求简单(如:每天备份一次数据、每小时清理一次缓存),直接使用 Spring
@Scheduled即可,它足够简单且好用。 -
如果你的应用对定时任务有更高要求,
- 需要任务在服务器重启后依然存在(持久化)。
- 需要动态地修改任务的执行时间或禁用/启用任务。
- 需要构建集群环境,确保任务的高可用性。
- 那么选择 Quartz 是一个稳妥且强大的方案。
-
如果你的应用是微服务架构,
- 你需要在多个服务实例上协同执行一个任务,但又不希望所有实例都执行一遍(避免重复)。
- 你需要将一个大任务拆分成多个小任务,并行处理以提高效率(分片)。
- 那么毫无疑问,Elastic-Job 是为你量身定做的分布式任务调度解决方案。
