Spring cron 与普通 cron 的区别是什么?

143

我正在尝试在一个遗留的Java/Spring/Hibernate项目中运行cron作业,所以我决定使用spring调度程序。

我希望myTask.doStuff在每月第一个星期日的12:00运行。

在我的application-context.xml文件中,我已经配置了我的任务调度程序:

<task:scheduled-tasks scheduler="MyTaskScheduler">
    <task:scheduled ref="myTask" method="doStuff" cron="0 0 12 ? 1/1 SUN#1 *"/> <!-- Every first Sundy of the month -->
</task:scheduled-tasks>

<task:scheduler id="MyTaskScheduler" pool-size="10"/>

问题在于cron表达式本身:0 0 12 ? 1/1 SUN#1 *

myTask是一个bean,其中有一个名为doStuff的方法,在单元测试中运行得非常完美。

但是当我构建和部署时,Spring抛出了一个启动时异常:

Caused by: java.lang.IllegalArgumentException: cron expression must consist of 6 fields (found 7 in 0 0 12 ? 1/1 SUN#1 *)
at org.springframework.scheduling.support.CronSequenceGenerator.parse(CronSequenceGenerator.java:233)
at org.springframework.scheduling.support.CronSequenceGenerator.<init>(CronSequenceGenerator.java:81)
at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:54)
at org.springframework.scheduling.support.CronTrigger.<init>(CronTrigger.java:44)
at org.springframework.scheduling.config.ScheduledTaskRegistrar.afterPropertiesSet(ScheduledTaskRegistrar.java:129)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)

鉴于我第一次使用cron表达式,我的第一个假设是我做错了什么,但我使用cronmaker进行了双重检查,结果相同。

所有文档都说:cron表达式是由六个或七个子表达式(字段)组成的字符串。1

尽管如此,我尝试去掉第7个元素(年份),因为它不在任何示例中,并得到了一个不同的错误消息:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.scheduling.config.ScheduledTaskRegistrar#0': Invocation of init method failed; nested exception is java.lang.NumberFormatException: For input string: "0#1"

org.springframework.scheduling支持与其他所有内容不同的cron风格吗?spring-specific documentation 只是说“cron expressions”。

在此情况下,如何使此cron表达式按预期工作?任何帮助都将不胜感激。

目前,我的解决方案是将此表达式简化为每个星期日运行一次,并添加一些Java逻辑以计算月份的哪个星期日,然后查看是否有效 - 但这似乎违背了配置方法的初衷,而且看起来像一个反模式。


你尝试过 0 0 12 ? * SUN#1 吗?(去掉最后的 * - Tom
是的,我得到了一个不同的错误信息 - 我在提到“尽管如此,我尝试去掉第七个元素”时列出了错误信息。 :) - Paul
Spring调度程序不支持过于复杂的cron表达式(不确定这里是否适用),就像Quartz调度程序一样。建议使用Quartz。 - Evgeni Dimitrov
@Evgeni,那么CRON有不同的“口味”吗?SpringScheduler的Cron表达式不能够胜任这个任务吗?(如果可以避免,我不想切换依赖项) - Paul
2个回答

184

Spring定时任务的格式与cron表达式不同。

它们不遵循UNIX cron表达式的格式。

只有6个字段:

  • 秒 (second),
  • 分 (minute),
  • 时 (hour),
  • 月中日 (day of month),
  • 月份 (month),
  • 星期中的天数 (day(s) of week)。

星号 (*) 表示匹配任何值。 */X 表示“每 X”(见下面的示例)。

对我来说,数字星期几不起作用。此外,“MON-FRI”更容易阅读。 以下是一些示例表达式:

"0 0 18 * * MON-FRI" means every weekday at 6:00 PM. 

"0 0 */1 * * *" means every hour on the hour.

"0 0 */8 * * *" means every 8 hours on the hour.

"0 0 12 1 * *" means 12:00 PM on the first day of every month. 

在这里您可以找到一些附加信息

您还可以发现Spring文档很有用。


1
认为这是更新后的第二个网址: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/integration.html#scheduling - Scala Enthusiast
4
还有一个不一致之处 - 在春季,'day'和'day-of-week'字段使用AND逻辑组合,但在cron中使用OR。 - Eugene
3
你们知道是否可能将cron表达式转换为Spring格式吗? - Viny Machado
您还可以在Spring Cron中添加时区表达式。 - anataliocs
完美的答案! - Gaurav

144

参考资料:https://www.baeldung.com/cron-expressions

Spring定时任务的格式如下:

1 2 3 4 5 6 Index
- - - - - -
* * * * * * command to be executed
- - - - - -
| | | | | | 
| | | | | ------- Day of week (MON - SUN)
| | | | --------- Month (1 - 12)
| | | ----------- Day of month (1 - 31)
| |-------------- Hour (0 - 23)
| --------------- Minute (0 - 59)
----------------- Seconds (0 - 59)

Linux Cron工作的格式如下:

From: https://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/

1 2 3 4 5 Index
- - - - -
* * * * * command to be executed
- - - - -
| | | | |
| | | | ----- Day of week (0 - 7) (Sunday=0 or 7)
| | | ------- Month (1 - 12)
| | --------- Day of month (1 - 31)
| ----------- Hour (0 - 23)
------------- Minute (0 - 59)

注意:

  • 有些文章称可以使用年份(year)作为第七个可选参数,但是我尝试使用最新版本的Spring时却遇到了错误,因此我认为这种方法并不可行。
  • 如果您Linux cron job表达式足够简单,则可以在前面加上0,它将转换为Spring计划任务表达式
    • 例如,每5分钟
      • */5 * * * * Linux cron job
      • 0 */5 * * * * Spring scheduled tasks

奖励内容:Spring Schedule Cron生成器

  1. 点击Show code snippet按钮
  2. 点击Run Code snippet按钮
  3. 尽情享受吧!

$('.select2').select2({
  width: '100%'
});

//// Init ////////////
$dropdown = $("#secondsSelect");
for (let i = 1; i < 60; i++) {
  $dropdown.append($("<option />").val(i).text(i));
}
$dropdown = $("#minSelect");
for (let i = 1; i < 60; i++) {
  $dropdown.append($("<option />").val(i).text(i));
}
$dropdown = $("#hoursSelect");
for (let i = 1; i < 24; i++) {
  $dropdown.append($("<option />").val(i).text(i));
}
$dropdown = $("#dayOfMonthSelect");
for (let i = 1; i < 32; i++) {
  $dropdown.append($("<option />").val(i).text(i));
}
//// Init End ////////////


$('.select2').on('select2:select', function(e) {
  let value = e.params.data.id;
  let prevValue = $(this).val().length > 0 ? $(this).val()[0] : null;

  if (value != parseInt(value)) {
    $(this).val(value).change();
  } else if (prevValue != parseInt(prevValue)) {
    $(this).val(value).change();
  }
  calculateSpringCron();
});

let r = function(dropdown) {
  return dropdown.val().join(",");

}

let calculateSpringCron = function() {

  let result = [
    r($("#secondsSelect")),
    r($("#minSelect")),
    r($("#hoursSelect")),
    r($("#dayOfMonthSelect")),
    r($("#monthsSelect")),
    r($("#weekdaySelect")),
  ];

  $("#result").val(result.join(" "));

  $("#result-expand").html(result.join(" &nbsp; &nbsp;"))

}

calculateSpringCron();
.ms-container {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding-left: 3em;
  padding-right: 3em;
  background: none !important;
  padding-bottom: 5em;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/multi-select/0.9.12/css/multi-select.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/multi-select/0.9.12/js/jquery.multi-select.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.8/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.8/js/select2.min.js"></script>

<div class="row">
  <div class="col-12">
    <h1>Spring Schedule Cron Generator</h1>
  </div>
</div>
<div class="row">
  <div class="col-4">
    Seconds:
    <select id="secondsSelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every seconds</option>
      <option value="*/2">Every even seconds</option>
      <option value="1-59/2">Every odd seconds</option>
      <option value="*/5">Every 5 seconds</option>
      <option value="*/10">Every 10 seconds</option>
      <option value="*/15">Every 15 seconds</option>
      <option value="*/30">Every 30 seconds</option>
    </select>
  </div>
  <div class="col-4">
    Minutes:
    <select id="minSelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every minutes</option>
      <option value="*/2">Every even minutes</option>
      <option value="1-59/2">Every odd minutes</option>
      <option value="*/5">Every 5 minutes</option>
      <option value="*/10">Every 10 minutes</option>
      <option value="*/15">Every 15 minutes</option>
      <option value="*/30">Every 30 minutes</option>
    </select>
  </div>
  <div class="col-4">
    Hours:
    <select id="hoursSelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every hour</option>
      <option value="*/2">Every even hour</option>
      <option value="1-11/2">Every odd hour</option>
      <option value="*/3">Every 3 hour</option>
      <option value="*/4">Every 4 hour</option>
      <option value="*/6">Every 6 hour</option>
    </select>
  </div>
</div>

<div class="row">
</div>

<div class="row">
  <div class="col-4">
    Days of month:
    <select id="dayOfMonthSelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every day of month</option>
      <option value="*/2">Even day of month</option>
      <option value="1-31/2">Odd day of month</option>
      <option value="*/5">Every 5 days of month (5,10,15...)</option>
      <option value="*/10">Every 10 days of month (10,20,30...)</option>
    </select>
  </div>
  <div class="col-4">
    Months:
    <select id="monthsSelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every month</option>
      <option value="*/2">Even months</option>
      <option value="1-11/2">Odd months</option>
      <option value="*/4">Every 4 months</option>
      <option value="*/6">Every 6 months(half year)</option>
      <option value="1">Jan</option>
      <option value="2">Feb</option>
      <option value="3">Mar</option>
      <option value="4">Apr</option>
      <option value="5">May</option>
      <option value="6">Jun</option>
      <option value="7">Jul</option>
      <option value="8">Aug</option>
      <option value="9">Sep</option>
      <option value="10">Oct</option>
      <option value="11">Nov</option>
      <option value="12">Dec</option>
    </select>
  </div>
  <div class="col-4">
    Weekday:
    <select id="weekdaySelect" class="select2" name="states[]" multiple="multiple">
      <option value="*" selected="selected">Every weekday</option>
      <option value="MON-FRI">Weekdays (MON-FRI)</option>
      <option value="SAT,SUN">Weekend</option>
      <option value="SUN">SUN</option>
      <option value="MON">MON</option>
      <option value="TUE">TUE</option>
      <option value="WED">WED</option>
      <option value="THU">THU</option>
      <option value="FRI">FRI</option>
      <option value="SAT">SAT</option>
    </select>
  </div>
</div>
<div class="row">
  <div class="col-12">
    Result:
    <input id="result" class="form-control" /> With a bit of seperation for better viewing:<br/>
    <h1 id="result-expand"></h1>
  </div>
</div>


1
另一种选择是JobRunr:您可以使用注释@Recurring(id = "my-recurring-job", cron = "*/5 * * * *")标记所有CRON方法。最大的好处是它附带了一个方便的仪表板,显示您的CRON作业是否成功。 - rdehuyss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接