Quartz里写了个定时任务,启动直接抛异常,提示「Based on configured schedule, the given trigger will never fire」。多数情况不是代码问题,是crontrigger的表达式没写对。先记字段结构,再看特殊字符,最后验证,顺序别乱。

字段顺序和取值范围

它按6到7个字段解析:秒、分、时、日、月、周,最后的年可以省略。秒和分取0到59,时取0到23,日取1到31,月可以写数字,也可以写JAN到DEC的缩写。最容易错的是日和周这两个位置,必须有一个写成问号,另一个才生效,两个都写星号可能直接不触发。

0 0 2 * * ?
0 30 8 ? * MON-FRI
0 0/30 9-18 * * ?

第一行是每天凌晨2点整,第二行是工作日早上8点半,第三行是9点到18点之间每半小时一次。数字带斜杠表示从起点按步长循环,小时位写0/2就是从0点起每隔两小时跑一次。

特殊字符速查

  • 逗号列举多个值,比如分位写0,15,30,45
  • 连字符表区间,比如周位MON-FRI
  • L在日位代表当月最后一天,在周位代表周六
  • W代表离指定日最近的工作日,井号用于第几个周几,比如6#3是第三个周五

这些符号不用全背,日常任务大多只用星号、问号、斜杠和连字符,复杂的月底报表类需求才会用到L和W。

数字含义容易踩的坑

Quartz的周字段里,1代表周日而不是周一,7才是周六,跟不少人的直觉相反。另外它和Spring的@Scheduled表达式很像但不完全兼容,字段含义和特殊字符的支持有差别,两边迁移时照搬容易出问题。

上线前先验证

  • 把表达式交给CronScheduleBuilder构建一次,抛异常就是格式问题
  • 调用trigger对象的getNextFireTime这类方法打印下次执行时间
  • 服务重启错过触发点会走misfire补偿策略,补跑还是跳过取决于配置

建议是:表达式写完先打印两轮下次触发时间再上线,比上线后盯着日志等报错省事得多。