Quartz:永远不会执行的Cron表达式


78

我知道有一个重复这里,这可能正是我的情况下,虽然它会值得一些更好的解释,我会尝试在这里提供。

我使用Spring应用程序上下文来处理Java Web应用程序。在这种情况下,我使用Quartz定义了计划作业。这些作业由.properties文件中定义的cron触发。

Spring上下文嵌入战争中,而.properties文件位于应用程序服务器(在这种情况下为Tomcat)上。

这很好,并允许根据环境(开发,集成,生产...)定义不同的克朗。

现在,当在我自己的计算机上本地运行此应用程序时,我不希望执行这些作业。有没有办法编写永远不会触发的cron表达式?


像其他问题一样,唯一的标准方法是以#注释字符开始命令。
Barmar 2012年

1
我可能还不清楚:尽管Spring / Quartz框架使用cron语法,但这不是crontab:cron用于XML字段:<property name="cronExpression" value="<expression>" /> 您不能通过注释行来取消激活作业。谢谢,不过。我将使用建议的方式指定到目前为止的未来计算机年份,因为我们知道它们将消失。

显然,Quartz cron表达式与Unix cron表达式并不十分相似,因为Unix cron没有秒或几年。cron标签可能不适用于此问题。
Barmar 2012年

您是对的,我已经遇到了一些差异。我将删除“ cron”标签。
印章

Answers:


73

TL; DR

在Quartz 1中,您可以使用以下cron :(59 59 23 31 12 ? 2099最后一个有效日期)。
在Quartz 2中,您可以使用以下cron:0 0 0 1 1 ? 2200

将来使用表达式

使用进行了一些快速测试org.quartz.CronExpression

String exp = "0 0 0 1 1 ? 3000";
boolean valid = CronExpression.isValidExpression(exp);
System.out.println(valid);
if (valid) {
    CronExpression cronExpression = new CronExpression(exp);
    System.out.println(cronExpression.getNextValidTimeAfter(new Date()));
}

当我这样做时String exp = "# 0 0 0 1 1 ?";isValid测试返回false

有了上面给出的示例,输出如下:

true
null

含义:

  • 该表达式有效;
  • 没有与该表达式匹配的即将到来的日期。

但是,要使调度程序接受cron触发器,后者必须与将来的日期匹配。

我尝试了几年,发现一旦年份超过2300,Quartz似乎就不再烦恼了(尽管我在Quartz 2的文档中没有提到该年份的最大值)。可能会有更清洁的方法来执行此操作,但这将满足我的需求。

因此,最后,我建议的时间表是0 0 0 1 1 ? 2200

石英1款

请注意,在Quartz 1中,2099是最后一个有效年份。因此,您可以调整cron表达式以使用Maciej Matys的建议59 59 23 31 12 ? 2099

替代方法:使用过去的日期

Arnaud Denoyelle提出了一些更优雅的建议,上面的测试证明了这一点是正确的表达方式:与其选择一个较远的日期,不如选择一个较远的日期:

0 0 0 1 1 ? 1970 (根据Quartz文档的第一个有效表达式)。

但是此解决方案不起作用。

hippofluff强调说Quartz过去将检测到一个表达式,将不再执行,因此将引发异常。

org.quartz.SchedulerException: Based on configured schedule, the given trigger will never fire.

这似乎已经在Quartz很久了

经验教训:测试不是万无一失的

这凸显了我的测试的弱点:如果要测试a CronExpression,请记住它必须具有nextValidTime1。否则,您将其传递给的调度程序将简单地拒绝它,并带有上述异常。

我建议修改测试代码如下:

String exp = "0 0 0 1 1 ? 3000";
boolean valid = CronExpression.isValidExpression(exp);
if (valid) {
    CronExpression cronExpression = new CronExpression(exp);
    valid = cronExpression.getNextValidTimeAfter(new Date()) != null;
}
System.out.println("Can I use <" + exp + ">? " + (valid ? "Go ahead!" : "This shall fail."));

到此为止:无需思考,只需阅读输出即可。


1这是我在测试Arnaud的解决方案时忘了的部分,这使我变得愚蠢,并证明我的测试不是靠我自己。


3
为什么不0 0 0 1 1 ? 1970呢?石英会拒绝吗?
Arnaud Denoyelle

5
Quartz 2.1.0抛出SchedulerException“基于配置的时间表,给定的触发器将永远不会归档”,并且在使用Arnaud的建议时,我的Grails应用程序启动失败
hippofluff 2015年

2
无需感觉,谢谢您更新答案!+1 :)仅供参考,将来某个日期允许的时间为100年。任何计划超过100年的计划都将无法进行。不知道为什么他们要定居100年,但是如果应用程序实际运行足够长的时间来触发我,我会印象深刻。尽管如此,即使这项技术甚至在100年内都
广为人知

2
我检查了代码[1]的年份魔术值(对我来说2200失败)(javadoc [2]说最大为2199,教程[3]说最大为2099)。实际的最大值是:public static final int MAX_YEAR = Calendar.getInstance()。get(Calendar.YEAR)+ 100; 因此,今年的最大值为2116。明年的最大值为2117 [1] fisheye.terracotta.org/browse/Quartz/trunk/quartz-core/src/main/… [2] quartz-scheduler.org/api/2.2。 1 / org / quartz / CronExpression.html [3] quartz-scheduler.org/documentation/quartz-2.x/tutorials/…– DelGurth
2016年

1
@Chop是的,谢谢,因为它易于使用。我在terracotta提出了一个文档错误,因为它目前令人困惑,即使在实际代码中,它们也使用不同的最长年份检查。
DelGurth '16

40

从技术上讲,可选的“石英年份”字段的有效值为1970-2099,因此2300不是预期值。我假设您确实需要执行此操作,并且您的Quartz版本尝试强制执行有效的cron语法(第1-31天,第1-12个月等)。

我当前在Resque-scheduler for Rails中使用以下代码,该代码以经过验证的crontab格式接受计划信息,以创建仅手动运行的测试作业:

cron: "0 5 31 2 *"

该作业将耐心等待2月31日清晨,然后再运行。对于Quartz crontrigger中的等效,请尝试以下行或其某些变体:

0 0 5 31 2 ?

我确实减少了一年,以便Quartz接受我给他的时间表。您的解决方案确实非常优雅。谢谢!
印章

9
哼,终于有机会测试这个建议了。似乎Quartz拒绝了该cron,因为它检测到它永远不会执行...

值得一试,因为相同的方法在Rails中似乎可以正常工作。您可能会考虑使用.startAt(startTime)触发器来考虑它早就开始,然后指示它仅在已经过去的特定年份触发。但这并不那么优雅。
Eric Tjossem 2013年

1
0 5 31 2 *一个似乎在Magento crons很好地工作。
toon81 2015年

3
这并不是一个错误的工作Invalid cron expression "0 0 5 31 2 ?" led to runaway search for next trigger
弗兰克·德雷克

25

试试这个: 59 59 23 31 12 ? 2099


您能解释一下用它代替它会带来什么好处0 0 0 1 1 ? 2200吗?据我所知,地雷将在2200年除夕触发,而你的距今只有一百年。我错了吗?如果不是,我认为将来越好,您同意吗?
印章

11
这是最后一个有效的Quartz表达式,您的Quartz拒绝至少开始工作Quartz 1.6和spring 2.5.6SEC3
Maciej Matys 2013年

我看了看文档,您是正确的,这是最后一个有效的表达式。尽管如此,0 0 0 1 1 ? 2200仍可与Quartz的最新版本一起使用。但是,提出了一个更智能的解决方案(请参阅我自己的答案中的编辑内容)。

7

我在尝试解决类似的问题(禁用cron表达式)时发现了这一点,但是遇到了需要有效的未来计划日期的问题。

我还使用7值语法遇到问题-无法在cron时间表中指定年份。

所以我用这个:0 0 3?2 MON#5

下次将执行以下操作:

  1. 2044年2月29日,星期一,凌晨3:00
  2. 2072年2月29日,星期一,凌晨3:00
  3. 2112年2月29日,星期一,凌晨3:00
  4. 2140年2月29日,星期一,凌晨3:00
  5. 2168年2月29日,星期一,凌晨3:00

因此,从本质上讲,出于所有意图和目的,它都被禁用。:)

啊。诅咒,这仅适用于Quartz调度程序语法-Spring CronTrigger语法不允许第五个星期一使用MON#5

那么,下一个最好的事情是0 0 3 29 2吗?只会在2月29日(le年)凌晨3点执行


这是我最喜欢的解决方法!!值得一枚徽章:-)
drizin

5

如果在表达式中使用@Scheduled(cron="")表达式(技术上不使用石英,而在春季那段时间很常见),则不能使用7字段的年度解决方案,但可以使用以下选项:

  • 如果您使用的是Spring 5.1+(springBoot 2.1+),则只需使用"${your.cron.prop:-}并且不要将该属性设置为禁用执行-请参阅@Scheduled。或将属性本身设置为“-”(如果使用yml,请确保使用引号)。
  • @Scheduled完全通过方法禁用Bean /服务,例如通过使用@ConditionalOnProperty("my.scheduleproperty.active")注释而不设置属性(或将其设置为false

2

现在,当在我自己的计算机上本地运行此应用程序时,我不希望执行这些作业。有没有办法编写永远不会触发的cron表达式?

如果要在计算机上禁用调度,则有几种方法可以实现。

首先,您可以将Quartz@Profile的配置移至基于配置,而不在本地启用此配置文件。如果配置文件未激活,Quartz根本不会启动。

另一种方法是将Quartz配置为不自动启动。还有一个SchedulerFactoryBean#setAutoStartup(),你可以在设置BeanPostProcessor中的一个开发的个人资料登记。尽管该线程已经很老了,但是Spring Boot通过注册SchedulerFactoryBeanCustomizerBean提供了另一种方法来做同样的事情。


0

嗨,您可以尝试执行此操作,它将永远不会像-cron中那样执行您的时间表

 @Scheduled(cron = "${schedular.cron.expression}")

schedular.cron.expression=-
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.