Spring Boot 写定时任务,发现了一个大坑!

简介: 最近栈长用 Spring Boot 写了一个定时任务:

最近栈长用 Spring Boot 写了一个定时任务:

@Scheduled(cron = "0/10 * * * * ? *")
public void execute() {
  ...
}

Spring Boot 实现定时任务确实很简单,其实是从 Spring 3.1 开始,定时任务的编写就变得非常简单,只需要几个注解就能快速开启计划任务的支持,具体可以看这篇文章:https://mp.weixin.qq.com/s/hsuCC93OJFc8URI517-umg


可是当我把代码写完,启动就报错:

Caused by: java.lang.IllegalStateException: Encountered invalid @Scheduled method 'xxx': Cron expression must consist of 6 fields (found 7 in "0/10 * * * * ? *")
  at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.processScheduled(ScheduledAnnotationBeanPostProcessor.java:511) ~[spring-context-5.3.1.jar:5.3.1]
  at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.lambda$null$1(ScheduledAnnotationBeanPostProcessor.java:374) ~[spring-context-5.3.1.jar:5.3.1]
  at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_261]
  at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.lambda$postProcessAfterInitialization$2(ScheduledAnnotationBeanPostProcessor.java:374) ~[spring-context-5.3.1.jar:5.3.1]
  at java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) ~[na:1.8.0_261]
  at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(ScheduledAnnotationBeanPostProcessor.java:373) ~[spring-context-5.3.1.jar:5.3.1]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:444) ~[spring-beans-5.3.1.jar:5.3.1]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792) ~[spring-beans-5.3.1.jar:5.3.1]
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:609) ~[spring-beans-5.3.1.jar:5.3.1]
  ... 16 common frames omitted

什么鬼?

错误描述大概是说我的 Cron 表达式有问题,Cron 表达式只能由 6 位组成,但我的表达式有 7 位,不能啊,表达式也确实没问题的啊。。。

点进去看下 @Scheduled 注解的 cron 参数源码:image.png

确实只有 6 位,并没有表示年份的占位。


继续看源码,CronSequenceGenerator 在 Spring 5.3 中已经标识废弃了,需要转到:


org.springframework.scheduling.support.CronExpression


再点进去看 CronExpression 类的源码:


image.png


6 个字段的意思很清楚了,具体可以参考:


https://www.manpagez.com/man/5/crontab/


示例表达式:

"0 0 * * * *" = the top of every hour of every day.
"*/10 * * * * *" = every ten seconds.
"0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
"0 0 6,19 * * *" = 6:00 AM and 7:00 PM every day.
"0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day.
"0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
"0 0 0 25 12 ?" = every Christmas Day at midnight
"0 0 0 L * *" = last day of the month at midnight
"0 0 0 L-3 * *" = third-to-last day of the month at midnight
"0 0 0 1W * *" = first weekday of the month at midnight
"0 0 0 LW * *" = last weekday of the month at midnight
"0 0 0 * * 5L" = last Friday of the month at midnight
"0 0 0 * * THUL" = last Thursday of the month at midnight
"0 0 0 ? * 5#2" = the second Friday in the month at midnight
"0 0 0 ? * MON#1" = the first Monday in the month at midnight

并且支持以下代替写法:

"@yearly" (or "@annually") to run un once a year, i.e. "0 0 0 1 1 *",
"@monthly" to run once a month, i.e. "0 0 0 1 * *",
"@weekly" to run once a week, i.e. "0 0 0 * * 0",
"@daily" (or "@midnight") to run once a day, i.e. "0 0 0 * * *",
"@hourly" to run once an hour, i.e. "0 0 * * * *".

如:每年:0 0 0 1 1 *,就可以写成:@yearly,除了每年,还有每月、每月、每周、每天、每小时,可惜……没有每分、每秒!


Spring Task 我们可以将它看成是一个轻量级的 Quartz,而且使用起来比 Quartz 要简单许多,使用了类似于 cron 的表达式,并扩展了平常的 UN*X 定义,看来和平常的 cron 表达式还不太一样,位数不同,也存在兼容问题,真坑。。。


既然知道了问题所在,再回到之前错误,那就只能把最后的表示 “年” 的字段去掉,6 位就够了,第7 位不要,默认就是表示每年。


那么问题来了,我想在具体的某年,如 2025/02/25 这天执行任务,咋整?


没办法,Spring Task 不能指定年份,那就只能换成 Quartz,Quartz 也更强大,所以,如果是简单的定时任务,Spring Task 就搞定了,复杂的建议还是使用 Quartz。


另外,注意,Spring Task 的 cron 表达式并不是完全兼容通常的 cron 表达式,最好根据以上官方的文档来编写,以免到了线上不执行那就完蛋了。


最后,觉得我的文章对你用收获的话,动动小手,给个在看、转发,原创不易,栈长需要你的鼓励。


相关文章
|
2月前
|
监控 Java BI
《深入理解Spring》定时任务——自动化调度的时间管理者
Spring定时任务通过@Scheduled注解和Cron表达式实现灵活调度,支持固定频率、延迟执行及动态配置,结合线程池与异常处理可提升可靠性,适用于报表生成、健康检查等场景,助力企业级应用自动化。
|
6月前
|
Java Spring
使用 Spring Boot 多个定时任务阻塞问题的解决方案
我是小假 期待与你的下一次相遇 ~
382 5
|
6月前
|
存储 前端开发 Java
|
11月前
|
Java 调度 Spring
Spring之定时任务基本使用篇
本文介绍了在Spring Boot项目中使用定时任务的基本方法。主要通过`@Scheduled`注解实现,需添加`@EnableScheduling`开启定时任务功能。文中详细解析了Cron表达式的语法及常见实例,如每秒、每天特定时间执行等。此外,还探讨了多个定时任务的执行方式(并行或串行)及其潜在问题,并留待后续深入讨论。
446 64
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
382 1
|
11月前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
2734 17
Spring Boot 两种部署到服务器的方式
|
9月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
386 0
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
Spring Cloud Alibaba 发布了 Scheduling 任务调度模块 [#3732]提供了一套开源、轻量级、高可用的定时任务解决方案,帮助您快速开发微服务体系下的分布式定时任务。
16039 117
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
575 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
786 2

热门文章

最新文章