SpringBoot3实战:5分钟搞定Quartz动态定时任务管理(含数据库配置) SpringBoot3实战5分钟搞定Quartz动态定时任务管理含数据库配置在当今快节奏的业务环境中定时任务已成为企业级应用不可或缺的一部分。无论是每天凌晨的数据报表生成还是每小时的系统状态检查亦或是每分钟的订单状态扫描定时任务都在默默地支撑着业务的正常运转。而Quartz作为Java领域最成熟的任务调度框架与SpringBoot3的结合更是如虎添翼让开发者能够轻松应对各种复杂的定时任务场景。1. 环境准备与基础配置1.1 项目依赖配置首先创建一个新的SpringBoot3项目在pom.xml中添加必要的依赖dependencies !-- SpringBoot基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Quartz集成依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-quartz/artifactId /dependency !-- 数据库相关依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies1.2 数据库配置在application.yml中配置数据库连接和Quartz的持久化设置spring: datasource: url: jdbc:mysql://localhost:3306/quartz_demo?useSSLfalseserverTimezoneUTC username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver quartz: job-store-type: jdbc jdbc: initialize-schema: always properties: org: quartz: scheduler: instanceName: clusteredScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate tablePrefix: QRTZ_ isClustered: true clusterCheckinInterval: 20000 useProperties: false threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5注意在实际生产环境中initialize-schema应该设置为never并手动执行Quartz提供的SQL脚本来创建表结构。2. 核心组件实现2.1 任务实体定义首先定义一个简单的任务实体用于存储任务配置信息Entity Table(name sys_job) public class SysJob { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String jobName; private String jobGroup; private String jobClass; private String cronExpression; private String description; private Boolean concurrent; private String status; // Getters and Setters }2.2 任务执行逻辑创建一个基础任务类所有具体任务都将继承这个类public abstract class BaseJob implements Job { private static final Logger logger LoggerFactory.getLogger(BaseJob.class); Override public void execute(JobExecutionContext context) throws JobExecutionException { long startTime System.currentTimeMillis(); logger.info(任务开始执行 - {}, context.getJobDetail().getKey().getName()); try { doExecute(context); } catch (Exception e) { logger.error(任务执行异常, e); throw new JobExecutionException(e); } long endTime System.currentTimeMillis(); logger.info(任务执行完成 - 耗时: {}ms, (endTime - startTime)); } protected abstract void doExecute(JobExecutionContext context) throws Exception; }2.3 具体任务示例实现一个具体的任务示例Component public class SampleJob extends BaseJob { Override protected void doExecute(JobExecutionContext context) throws Exception { // 这里编写具体的任务逻辑 System.out.println(SampleJob正在执行当前时间: new Date()); } }3. 动态任务管理3.1 任务调度服务创建一个任务调度服务封装Quartz的核心操作Service public class QuartzJobService { Autowired private Scheduler scheduler; /** * 创建并调度任务 */ public void scheduleJob(SysJob sysJob) throws Exception { // 构建JobDetail JobDetail jobDetail JobBuilder.newJob(getJobClass(sysJob.getJobClass())) .withIdentity(sysJob.getJobName(), sysJob.getJobGroup()) .withDescription(sysJob.getDescription()) .storeDurably() .build(); // 构建Trigger Trigger trigger TriggerBuilder.newTrigger() .withIdentity(sysJob.getJobName() _Trigger, sysJob.getJobGroup()) .withSchedule(CronScheduleBuilder.cronSchedule(sysJob.getCronExpression())) .build(); // 调度任务 scheduler.scheduleJob(jobDetail, trigger); } /** * 暂停任务 */ public void pauseJob(String jobName, String jobGroup) throws SchedulerException { JobKey jobKey new JobKey(jobName, jobGroup); scheduler.pauseJob(jobKey); } /** * 恢复任务 */ public void resumeJob(String jobName, String jobGroup) throws SchedulerException { JobKey jobKey new JobKey(jobName, jobGroup); scheduler.resumeJob(jobKey); } /** * 删除任务 */ public void deleteJob(String jobName, String jobGroup) throws SchedulerException { JobKey jobKey new JobKey(jobName, jobGroup); scheduler.deleteJob(jobKey); } /** * 立即执行一次任务 */ public void triggerJob(String jobName, String jobGroup) throws SchedulerException { JobKey jobKey new JobKey(jobName, jobGroup); scheduler.triggerJob(jobKey); } /** * 更新任务调度时间 */ public void updateJobCron(SysJob sysJob) throws SchedulerException { TriggerKey triggerKey TriggerKey.triggerKey(sysJob.getJobName() _Trigger, sysJob.getJobGroup()); // 获取触发器 CronTrigger trigger (CronTrigger) scheduler.getTrigger(triggerKey); // 重新构建触发器 trigger trigger.getTriggerBuilder() .withIdentity(triggerKey) .withSchedule(CronScheduleBuilder.cronSchedule(sysJob.getCronExpression())) .build(); // 重新调度任务 scheduler.rescheduleJob(triggerKey, trigger); } /** * 获取任务类 */ private Class? extends Job getJobClass(String className) throws ClassNotFoundException { return (Class? extends Job) Class.forName(className); } }3.2 任务管理API创建一个REST控制器提供任务管理的API接口RestController RequestMapping(/api/jobs) public class JobController { Autowired private QuartzJobService quartzJobService; Autowired private SysJobRepository sysJobRepository; /** * 创建任务 */ PostMapping public ResponseEntity? createJob(RequestBody SysJob sysJob) throws Exception { sysJobRepository.save(sysJob); quartzJobService.scheduleJob(sysJob); return ResponseEntity.ok().build(); } /** * 暂停任务 */ PostMapping(/{id}/pause) public ResponseEntity? pauseJob(PathVariable Long id) throws SchedulerException { SysJob sysJob sysJobRepository.findById(id).orElseThrow(); quartzJobService.pauseJob(sysJob.getJobName(), sysJob.getJobGroup()); return ResponseEntity.ok().build(); } /** * 恢复任务 */ PostMapping(/{id}/resume) public ResponseEntity? resumeJob(PathVariable Long id) throws SchedulerException { SysJob sysJob sysJobRepository.findById(id).orElseThrow(); quartzJobService.resumeJob(sysJob.getJobName(), sysJob.getJobGroup()); return ResponseEntity.ok().build(); } /** * 删除任务 */ DeleteMapping(/{id}) public ResponseEntity? deleteJob(PathVariable Long id) throws SchedulerException { SysJob sysJob sysJobRepository.findById(id).orElseThrow(); quartzJobService.deleteJob(sysJob.getJobName(), sysJob.getJobGroup()); sysJobRepository.delete(sysJob); return ResponseEntity.ok().build(); } /** * 更新任务调度时间 */ PutMapping(/{id}/cron) public ResponseEntity? updateJobCron(PathVariable Long id, RequestBody String cronExpression) throws SchedulerException { SysJob sysJob sysJobRepository.findById(id).orElseThrow(); sysJob.setCronExpression(cronExpression); sysJobRepository.save(sysJob); quartzJobService.updateJobCron(sysJob); return ResponseEntity.ok().build(); } /** * 立即执行一次任务 */ PostMapping(/{id}/trigger) public ResponseEntity? triggerJob(PathVariable Long id) throws SchedulerException { SysJob sysJob sysJobRepository.findById(id).orElseThrow(); quartzJobService.triggerJob(sysJob.getJobName(), sysJob.getJobGroup()); return ResponseEntity.ok().build(); } }4. 高级特性与最佳实践4.1 集群环境下的任务调度在集群环境下Quartz的集群功能可以确保任务不会被多个节点重复执行。要实现这一点需要确保所有节点使用相同的数据库配置设置org.quartz.jobStore.isClusteredtrue为每个节点配置唯一的instanceIdspring: quartz: properties: org: quartz: scheduler: instanceId: ${spring.application.name}-${random.uuid} jobStore: isClustered: true4.2 任务执行日志记录为了追踪任务执行情况可以创建一个任务执行日志实体Entity Table(name sys_job_log) public class SysJobLog { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; ManyToOne JoinColumn(name job_id) private SysJob job; private Date startTime; private Date endTime; private Long executionTime; private String status; private String exception; // Getters and Setters }然后在BaseJob中记录执行日志public abstract class BaseJob implements Job { Autowired private SysJobLogRepository jobLogRepository; Override public void execute(JobExecutionContext context) throws JobExecutionException { SysJobLog jobLog new SysJobLog(); jobLog.setStartTime(new Date()); try { doExecute(context); jobLog.setStatus(SUCCESS); } catch (Exception e) { jobLog.setStatus(FAILED); jobLog.setException(ExceptionUtils.getStackTrace(e)); throw new JobExecutionException(e); } finally { jobLog.setEndTime(new Date()); jobLog.setExecutionTime(jobLog.getEndTime().getTime() - jobLog.getStartTime().getTime()); jobLogRepository.save(jobLog); } } protected abstract void doExecute(JobExecutionContext context) throws Exception; }4.3 任务执行超时处理为了防止任务长时间运行导致系统资源耗尽可以实现任务超时控制public abstract class BaseJob implements Job { Value(${quartz.job.timeout:300000}) // 默认5分钟超时 private long timeout; Override public void execute(JobExecutionContext context) throws JobExecutionException { ExecutorService executor Executors.newSingleThreadExecutor(); Future? future executor.submit(() - { try { doExecute(context); } catch (Exception e) { throw new RuntimeException(e); } }); try { future.get(timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { future.cancel(true); throw new JobExecutionException(任务执行超时); } catch (Exception e) { throw new JobExecutionException(e); } finally { executor.shutdownNow(); } } protected abstract void doExecute(JobExecutionContext context) throws Exception; }5. 常见问题与解决方案5.1 任务错过触发(Misfire)处理当任务因为系统关闭或线程不足而错过触发时间时Quartz提供了多种处理策略策略说明适用场景MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY忽略错过触发立即执行需要尽快执行的任务MISFIRE_INSTRUCTION_FIRE_NOW立即触发一次执行错过一次执行的任务MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT立即重新调度保留剩余执行次数固定次数的任务MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT立即重新调度保留剩余执行次数固定次数的任务MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT下次调度时间执行保留剩余执行次数周期性任务MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT下次调度时间执行保留剩余执行次数周期性任务在代码中可以这样配置Trigger trigger TriggerBuilder.newTrigger() .withIdentity(trigger1, group1) .withSchedule(CronScheduleBuilder.cronSchedule(0 0/5 * * * ?) .withMisfireHandlingInstructionFireAndProceed()) .build();5.2 数据库连接池配置Quartz在频繁操作数据库时可能会遇到连接池问题建议配置专用的连接池spring: quartz: properties: org: quartz: jobStore: dataSource: quartzDataSource dataSource: quartzDataSource: provider: hikaricp driver: com.mysql.cj.jdbc.Driver URL: jdbc:mysql://localhost:3306/quartz_demo user: root password: yourpassword maxConnections: 10 validationQuery: select 15.3 任务并发控制默认情况下Quartz允许同一JobDetail的多个实例并发执行。如果需要禁止并发执行可以在Job类上添加DisallowConcurrentExecution注解DisallowConcurrentExecution public class NonConcurrentJob extends BaseJob { // 实现略 }