本文目录导读:

- 项目依赖配置(pom.xml)
- 主启动类
- 方式一:使用@Scheduled注解(简单定时任务)
- 方式二:使用ScheduleConfig配置定时任务
- 方式三:使用Quartz框架(分布式定时任务)
- 动态定时任务管理
- 配置文件方式
- 使用配置文件属性的定时任务
- 线程池配置
- 测试类
- 使用说明
- 注意事项
我来为您提供一个完整的Spring Boot定时任务案例,包括多种实现方式。
项目依赖配置(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>scheduled-task-demo</artifactId>
<version>1.0.0</version>
<name>scheduled-task-demo</name>
<description>Spring Boot定时任务示例</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Lombok 简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 如果使用Quartz,需要添加以下依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
主启动类
package com.example.scheduled;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // 启用定时任务
public class ScheduledTaskApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduledTaskApplication.class, args);
}
}
方式一:使用@Scheduled注解(简单定时任务)
1 基础定时任务类
package com.example.scheduled.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 基础定时任务示例
*/
@Slf4j
@Component
public class SimpleScheduledTasks {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 固定速率执行(从任务开始时间计算)
* 每5秒执行一次
*/
@Scheduled(fixedRate = 5000)
public void fixedRateTask() throws InterruptedException {
log.info("固定速率任务开始, 时间: {}", LocalDateTime.now().format(FORMATTER));
Thread.sleep(2000); // 模拟任务执行时间
log.info("固定速率任务结束, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 固定延迟执行(从任务结束时间计算)
* 上一次任务执行完成后3秒再执行
*/
@Scheduled(fixedDelay = 3000)
public void fixedDelayTask() {
log.info("固定延迟任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 初始延迟 + 固定延迟
* 启动5秒后执行,之后每次执行完毕后延迟2秒
*/
@Scheduled(initialDelay = 5000, fixedDelay = 2000)
public void initialDelayTask() {
log.info("初始延迟任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 使用cron表达式
* 每秒执行一次
*/
@Scheduled(cron = "0/1 * * * * ?")
public void cronTask1() {
log.info("每秒执行的任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 使用cron表达式
* 每天上午10点15分执行
*/
@Scheduled(cron = "0 15 10 ? * *")
public void cronTask2() {
log.info("每天10:15执行的任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 使用cron表达式
* 每周一至周五的上午10:15执行
*/
@Scheduled(cron = "0 15 10 ? * MON-FRI")
public void cronTask3() {
log.info("工作日10:15执行的任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
/**
* 使用cron表达式
* 每月1号和15号的上午10:15执行
*/
@Scheduled(cron = "0 15 10 1,15 * ?")
public void cronTask4() {
log.info("每月1号、15号10:15执行的任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}
}
2 Cron表达式详解
package com.example.scheduled.config;
/**
* Cron表达式说明
*
* Cron表达式格式:秒 分钟 小时 日 月 星期 [年]
*
* 字段说明:
* 秒(0-59),分钟(0-59),小时(0-23),
* 日(1-31),月(1-12或JAN-DEC),
* 星期(1-7或SUN-SAT),年(可选)
*
* 特殊字符:
* * :任意值
* ? :不指定值(用于日或星期)
* - :区间
* / :间隔
* , :列表
* L :
* W :工作日
* # :星期
*/
public class CronExpressionExample {
// 常用示例
/*
* "0 0 12 * * ?" 每天中午12点触发
* "0 15 10 ? * *" 每天上午10:15触发
* "0 15 10 * * ?" 每天上午10:15触发
* "0 15 10 * * ? *" 每天上午10:15触发
* "0 15 10 * * ? 2019" 2019年的每天上午10:15触发
* "0 * 14 * * ?" 每天下午2点到2:59期间的每1分钟触发
* "0 0/5 14 * * ?" 每天下午2点到2:55期间的每5分钟触发
* "0 0/5 14,18 * * ?" 每天下午2点到2:55和6点到6:55期间的每5分钟触发
* "0 0-5 14 * * ?" 每天下午2点到2:05期间的每1分钟触发
* "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
* "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
* "0 15 10 15 * ?" 每月15日上午10:15触发
* "0 15 10 L * ?" 每月最后一日的上午10:15触发
* "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
* "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发
*/
}
方式二:使用ScheduleConfig配置定时任务
1 定时任务配置类
package com.example.scheduled.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.context.annotation.Bean;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Executors;
/**
* 定时任务配置类
* 可以动态添加定时任务
*/
@Slf4j
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 配置线程池
*/
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10); // 线程池大小
scheduler.setThreadNamePrefix("scheduled-task-"); // 线程名称前缀
scheduler.setAwaitTerminationSeconds(60); // 优雅关闭等待时间
scheduler.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
return scheduler;
}
/**
* 配置定时任务注册器
*/
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
// 动态添加定时任务
taskRegistrar.addFixedRateTask(() -> {
log.info("动态固定速率任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}, 10000, 5000); // 初始延迟10秒,固定速率5秒
taskRegistrar.addFixedDelayTask(() -> {
log.info("动态固定延迟任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}, 10000, 3000); // 初始延迟10秒,固定延迟3秒
taskRegistrar.addCronTask(() -> {
log.info("动态Cron任务, 时间: {}", LocalDateTime.now().format(FORMATTER));
}, "0/30 * * * * ?"); // 每30秒执行
}
}
方式三:使用Quartz框架(分布式定时任务)
1 Quartz任务类
package com.example.scheduled.quartz;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* Quartz任务示例
*/
@Slf4j
public class QuartzTask implements Job {
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.info("Quartz任务执行, 时间: {}", LocalDateTime.now().format(FORMATTER));
// 获取JobDataMap中的参数
String param = context.getJobDetail().getJobDataMap().getString("param");
log.info("任务参数: {}", param);
// 执行具体的业务逻辑
try {
// 模拟业务处理
Thread.sleep(1000);
log.info("Quartz任务执行完成");
} catch (InterruptedException e) {
log.error("任务执行失败", e);
Thread.currentThread().interrupt();
}
}
}
2 Quartz配置类
package com.example.scheduled.config;
import com.example.scheduled.quartz.QuartzTask;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Quartz配置类
*/
@Configuration
public class QuartzConfig {
/**
* 创建JobDetail
*/
@Bean
public JobDetail quartzJobDetail() {
return JobBuilder.newJob(QuartzTask.class)
.withIdentity("quartzTask") // Job名称
.usingJobData("param", "test-param") // 传递参数
.storeDurably() // 即使没有Trigger关联也保留
.build();
}
/**
* 创建Trigger
*/
@Bean
public Trigger quartzTrigger() {
// 每10秒执行一次
CronScheduleBuilder cronSchedule =
CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(quartzJobDetail())
.withIdentity("quartzTrigger") // Trigger名称
.withSchedule(cronSchedule)
.build();
}
}
3 使用Scheduler动态调度
package com.example.scheduled.service;
import com.example.scheduled.quartz.QuartzTask;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
/**
* 动态调度服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class QuartzSchedulerService {
private final Scheduler scheduler;
/**
* 启动时初始化
*/
@PostConstruct
public void init() {
try {
scheduler.start();
} catch (SchedulerException e) {
log.error("Scheduler启动失败", e);
}
}
/**
* 添加定时任务
*/
public void addJob(String jobName, String cronExpression) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(QuartzTask.class)
.withIdentity(jobName)
.usingJobData("param", jobName)
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger")
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(jobDetail, trigger);
log.info("添加定时任务: {}, cron: {}", jobName, cronExpression);
}
/**
* 暂停定时任务
*/
public void pauseJob(String jobName) throws SchedulerException {
scheduler.pauseJob(JobKey.jobKey(jobName));
log.info("暂停定时任务: {}", jobName);
}
/**
* 恢复定时任务
*/
public void resumeJob(String jobName) throws SchedulerException {
scheduler.resumeJob(JobKey.jobKey(jobName));
log.info("恢复定时任务: {}", jobName);
}
/**
* 删除定时任务
*/
public void deleteJob(String jobName) throws SchedulerException {
scheduler.deleteJob(JobKey.jobKey(jobName));
log.info("删除定时任务: {}", jobName);
}
}
动态定时任务管理
package com.example.scheduled.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
/**
* 动态定时任务管理控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/task")
@RequiredArgsConstructor
public class DynamicTaskController {
private final TaskScheduler taskScheduler;
// 存储动态任务
private final Map<String, ScheduledFuture<?>> taskMap = new ConcurrentHashMap<>();
/**
* 创建任务调度器
*/
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("dynamic-task-");
return scheduler;
}
/**
* 添加动态任务
*/
@PostMapping("/add")
public String addTask(@RequestParam String taskName,
@RequestParam String cron) {
// 检查任务是否已存在
if (taskMap.containsKey(taskName)) {
return "任务已存在: " + taskName;
}
// 创建定时任务
ScheduledFuture<?> future = taskScheduler.schedule(() -> {
log.info("动态任务[{}]执行, 时间: {}", taskName,
java.time.LocalDateTime.now());
}, new CronTrigger(cron));
taskMap.put(taskName, future);
log.info("添加动态任务: {}, cron: {}", taskName, cron);
return "任务添加成功: " + taskName;
}
/**
* 删除动态任务
*/
@DeleteMapping("/cancel")
public String cancelTask(@RequestParam String taskName) {
ScheduledFuture<?> future = taskMap.get(taskName);
if (future != null) {
future.cancel(true);
taskMap.remove(taskName);
log.info("取消动态任务: {}", taskName);
return "任务取消成功: " + taskName;
}
return "任务不存在: " + taskName;
}
/**
* 获取所有任务
*/
@GetMapping("/list")
public Map<String, ScheduledFuture<?>> getTaskList() {
return taskMap;
}
}
配置文件方式
1 application.yml配置
spring:
application:
name: scheduled-task-demo
# Quartz配置
quartz:
job-store-type: memory # 使用内存存储任务
wait-for-jobs-to-complete-on-shutdown: true # 关闭时等待任务完成
overwrite-existing-jobs: false # 不覆盖已存在的任务
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
threadPool:
maxConcurrency: 10
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jobStore:
class: org.quartz.simpl.RAMJobStore # 内存存储
# 自定义配置
scheduled:
# 定时任务开关
enabled: true
# 任务线程池大小
pool-size: 10
# 固定速率任务间隔(毫秒)
fixed-rate-interval: 5000
# 固定延迟任务间隔(毫秒)
fixed-delay-interval: 3000
# 初始延迟(毫秒)
initial-delay: 1000
使用配置文件属性的定时任务
package com.example.scheduled.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 配置文件属性定时任务
*/
@Slf4j
@Component
public class ConfigScheduledTasks {
@Value("${scheduled.fixed-rate-interval}")
private long fixedRateInterval;
@Value("${scheduled.fixed-delay-interval}")
private long fixedDelayInterval;
@Value("${scheduled.initial-delay}")
private long initialDelay;
/**
* 动态读取配置的固定速率
*/
@Scheduled(fixedRateString = "${scheduled.fixed-rate-interval}")
public void fixedRateWithConfig() {
log.info("配置固定速率任务, 间隔: {}ms", fixedRateInterval);
}
/**
* 动态读取配置的固定延迟
*/
@Scheduled(fixedDelayString = "${scheduled.fixed-delay-interval}")
public void fixedDelayWithConfig() {
log.info("配置固定延迟任务, 延迟: {}ms", fixedDelayInterval);
}
/**
* 动态读取配置的初始延迟
*/
@Scheduled(initialDelayString = "${scheduled.initial-delay}",
fixedDelayString = "${scheduled.fixed-delay-interval}")
public void initialDelayWithConfig() {
log.info("配置初始延迟任务, 初始延迟: {}ms", initialDelay);
}
}
线程池配置
package com.example.scheduled.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线程池配置
*/
@Configuration
public class ThreadPoolConfig {
/**
* 任务执行线程池
*/
@Bean(name = "scheduledTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程数
executor.setMaxPoolSize(20); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setKeepAliveSeconds(60); // 线程空闲时间
executor.setThreadNamePrefix("task-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
/**
* 定时任务调度器
*/
@Bean(name = "scheduledTaskScheduler")
public ThreadPoolTaskScheduler scheduledTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("scheduled-scheduler-");
scheduler.setWaitForTasksToCompleteOnShutdown(true);
scheduler.setAwaitTerminationSeconds(60);
scheduler.setRemoveOnCancelPolicy(true);
scheduler.initialize();
return scheduler;
}
}
测试类
package com.example.scheduled;
import com.example.scheduled.service.QuartzSchedulerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ScheduledTaskApplicationTests {
@Autowired
private QuartzSchedulerService quartzSchedulerService;
@Test
void contextLoads() {
}
@Test
void testQuartz() throws Exception {
// 添加一个定时任务
quartzSchedulerService.addJob("testJob", "0/5 * * * * ?");
Thread.sleep(10000); // 等待10秒
// 暂停任务
quartzSchedulerService.pauseJob("testJob");
Thread.sleep(5000);
// 恢复任务
quartzSchedulerService.resumeJob("testJob");
Thread.sleep(5000);
// 删除任务
quartzSchedulerService.deleteJob("testJob");
}
}
使用说明
- @Scheduled注解方式:适合简单的定时任务,使用方便,直接注解即可
- SchedulingConfigurer方式:适合需要动态添加任务的场景
- Quartz方式:适合复杂的定时任务场景,支持分布式、持久化等
- 动态任务管理:适合需要运行时动态增删任务的场景
注意事项
- 定时任务默认是单线程执行的,需要配置线程池
@Scheduled的fixedRate从任务开始时间计算,fixedDelay从任务结束时间计算- Cron表达式要确保正确,否则可能导致任务不执行
- 在分布式环境下,需要考虑任务的幂等性和分布式锁的问题
这个案例提供了多种定时任务的实现方式,您可以根据实际需求选择合适的方式。