
Quartz 2.3+ 与 Spring Boot 2.5+ 的依赖冲突怎么解
直接上结论:Spring Boot 2.5+ 内置的 spring-boot-starter-quartz 已经绑定了 Quartz 2.3.x,**不要手动引入 quartz 或 quartz-jobs 的独立版本**,否则大概率触发 NoClassDefFoundError: org/quartz/JobBuilder 或 IllegalAccessError。
常见错误现象是应用启动时报 java.lang.NoClassDefFoundError: org/quartz/SchedulerFactory,或定时任务压根不触发但无报错。
- 检查
pom.xml是否有显式声明quartz依赖 —— 删掉它 - 确认只保留
spring-boot-starter-quartz,让 Spring Boot 自动管理版本 - 若需自定义 Quartz 配置(如 JDBC JobStore),通过
application.yml中的spring.quartz.*配置项调整,而非 new SchedulerFactoryBean - Quartz 2.3+ 默认使用 RAMJobStore;切到数据库持久化时,注意
quartz.properties中的表名前缀必须与实际建表一致(如QRTZ_),否则调度器静默失败
Job 类必须用 @PersistJobDataAfterExecution 吗
不是必须,但**绝大多数带状态的 Job 必须加**。不加会导致 JobDataMap 在每次执行后被重置,上次写入的变量(比如计数器、临时 ID)全部丢失。
典型场景:一个每 5 秒跑一次的监控 Job,需要累计失败次数并达到阈值后告警。不加这个注解,jobExecutionContext.getJobDetail().getJobDataMap().put("failCount", count) 下次进来永远是初始值。
立即学习“Java免费学习笔记(深入)”;
- 搭配
@DisallowConcurrentExecution使用更安全,避免多实例并发修改同一份 JobDataMap - 如果 Job 是无状态的(比如只查 API、发邮件),可以不加,但建议统一加上,省得后续扩展时踩坑
- 注意:该注解只对
JobDataMap生效,不影响Trigger自带的参数(如trigger.getJobDataMap())
如何让 Quartz 在 Spring Boot 中正确加载 cron 表达式
不能直接在 @Scheduled(cron = "...") 里用 Quartz —— 这是 Spring 自带的 TaskScheduler,和 Quartz 无关。要用 Quartz,必须走 JobDetail + CronTrigger 路线,且表达式必须在代码或配置中明确指定。
最容易被忽略的是:cron 表达式字段顺序是 秒 分 时 日 月 周 年(Quartz 特有,比 Linux 多一位),而 Spring @Scheduled 是 秒 分 时 日 月 周(无年)。混用会直接导致调度时间错乱。
- 推荐方式:在
@Configuration类中用JobDetailFactoryBean和CronTriggerFactoryBean声明 Bean,其中cronExpression设为"0 0/30 * * * ?"(每半小时) - 若从配置文件读取,用
@Value("${job.cron:0 0 * * * ?}")注入,注意 YAML 中特殊字符要加引号 - 测试时别只看日志是否打印 —— 查
qrtz_fired_triggers表确认是否真触发,因为异常可能被 Quartz 吞掉(尤其未声明JobExecutionException)
为什么 Quartz 任务没报错却不执行
最大概率是 Scheduler 没启动,或者 JobDetail 没注册进调度器。Spring Boot 会自动调用 Scheduler.start(),但前提是它能扫描到你定义的 JobDetail 和 Trigger Bean。
常见表现:应用正常启动,控制台无 Quartz 相关日志(如 Started Quartz Scheduler),qrtz_job_details 表为空。
- 检查类是否在 Spring Boot 主类的包扫描路径下 —— 不在的话,
@Bean方法不会被识别 - 确认
Job类实现了org.quartz.Job接口,且是 public class(内部类或 lambda 不行) - 避免在
@PostConstruct里手动调用scheduler.scheduleJob(...)—— Spring Boot 的自动装配机制会覆盖它 - 开启 DEBUG 日志:
logging.level.org.quartz=DEBUG,重点看是否有Adding existing job或TriggerFired日志
最隐蔽的一点:Quartz 的线程池默认只有 1 个线程(org.quartz.threadPool.threadCount=1),如果某个 Job 执行超时或死锁,后续所有任务都会卡住 —— 检查 qrtz_fired_triggers 表里的 STATE 字段是否长期为 ACQUIRED。










