cornUtils 表达式动态生成

  • CronUtils 提供了 CronBuilder 类,可以灵活地构建 Cron 表达式。以下是对动态生成 Cron 表达式的详细说明和用法展开。

1. Cron 表达式的基本结构

Cron 表达式通常由多个字段组成,具体取决于使用的格式(如 Quartz、Unix 等)。以下是 Quartz 格式的字段及其含义:

秒 分 时 日 月 周 [年]
  • 秒 (0-59):表示每分钟的第几秒触发。
  • 分 (0-59):表示每小时的第几分钟触发。
  • 时 (0-23):表示每天的第几小时触发。
  • 日 (1-31):表示每月的第几天触发。
  • 月 (1-12 或 JAN-DEC):表示每年的第几个月触发。
  • 周 (0-7 或 SUN-SAT, 其中 0 和 7 都表示星期日):表示每周的哪一天触发。
  • 年 (可选):表示哪一年触发。

例如:

  • 0 30 14 * * ?:每天下午 2 点 30 分触发。
  • 0 0/5 * * * ?:每 5 分钟触发一次。

2. 使用 CronBuilder 动态生成 Cron 表达式

CronUtils 的 CronBuilder 类允许你通过编程方式动态构建 Cron 表达式。每个字段都可以通过工厂方法(如 FieldExpressionFactory)设置具体的值或规则。

示例代码:动态生成 Cron 表达式

import com.cronutils.builder.CronBuilder;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpressionFactory;

public class DynamicCronGeneration {

    public static void main(String[] args) {
        // 定义 Quartz 格式的 Cron
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        // 动态生成 Cron 表达式
        Cron cron = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))       // 秒:0
                .withMinute(FieldExpressionFactory.every(5))    // 分:每 5 分钟
                .withHour(FieldExpressionFactory.between(9, 17)) // 时:9 点到 17 点之间
                .withDoM(FieldExpressionFactory.every())        // 日:每天
                .withMonth(FieldExpressionFactory.every())      // 月:每月
                .withDoW(FieldExpressionFactory.questionMark()) // 周:不指定
                .instance();

        // 输出生成的 Cron 表达式
        System.out.println("生成的 Cron 表达式: " + cron.asString());
    }
}

运行结果:

生成的 Cron 表达式: 0 0/5 9-17 * * ?

3. CronBuilder 的核心方法

CronBuilder 提供了一系列方法来设置 Cron 表达式的各个字段。以下是常用的方法及其作用:

方法名描述
withSecond()设置秒字段。
withMinute()设置分字段。
withHour()设置时字段。
withDoM()设置日期字段(每月的第几天)。
withMonth()设置月份字段。
withDoW()设置星期字段(每周的哪一天)。
withYear()设置年份字段(可选)。

每个字段可以通过 FieldExpressionFactory 提供的静态方法设置具体的值或规则。


4. FieldExpressionFactory 的常用方法

FieldExpressionFactory 是用于生成字段表达式的工具类,支持多种规则。以下是常用的工厂方法及其作用:

方法名描述
on(value)指定固定值。例如:FieldExpressionFactory.on(30) 表示固定的 30 分钟。
every()表示“每一天”、“每一月”等。
every(interval)表示每隔一段时间。例如:FieldExpressionFactory.every(5) 表示每 5 分钟。
between(start, end)表示范围。例如:FieldExpressionFactory.between(9, 17) 表示 9 到 17 点之间。
questionMark()表示不指定(仅适用于日期和星期字段)。
lastDayOfMonth()表示每月的最后一天。
lastWeekdayOfMonth()表示每月的最后一个工作日。
nthDayOfWeek(n, day)表示某个月的第 n 个星期几。例如:FieldExpressionFactory.nthDayOfWeek(2, 1) 表示每个月的第二个星期一。

5. 动态生成 Cron 表达式的应用场景

(1) 用户自定义任务调度

假设用户可以通过界面选择任务的执行时间,例如:

  • 每天上午 9 点到下午 5 点之间,每 10 分钟执行一次。
  • 每月的最后一天凌晨 0 点执行。

可以根据用户输入动态生成对应的 Cron 表达式。

示例代码:根据用户输入生成 Cron 表达式

import com.cronutils.builder.CronBuilder;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpressionFactory;

public class UserDefinedCron {

    public static void main(String[] args) {
        // 用户输入
        int startHour = 9;   // 开始时间:9 点
        int endHour = 17;    // 结束时间:17 点
        int interval = 10;   // 每 10 分钟

        // 定义 Quartz 格式的 Cron
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        // 动态生成 Cron 表达式
        Cron cron = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))               // 秒:0
                .withMinute(FieldExpressionFactory.every(interval))      // 分:每 10 分钟
                .withHour(FieldExpressionFactory.between(startHour, endHour)) // 时:9 点到 17 点之间
                .withDoM(FieldExpressionFactory.every())                // 日:每天
                .withMonth(FieldExpressionFactory.every())              // 月:每月
                .withDoW(FieldExpressionFactory.questionMark())          // 周:不指定
                .instance();

        // 输出生成的 Cron 表达式
        System.out.println("生成的 Cron 表达式: " + cron.asString());
    }
}

运行结果:

生成的 Cron 表达式: 0 0/10 9-17 * * ?

(2) 复杂调度规则

例如:

  • 每月的最后一个工作日凌晨 2 点执行。
  • 每年的 1 月 1 日凌晨 0 点执行。
import com.cronutils.builder.CronBuilder;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpressionFactory;

public class ComplexCronExample {

    public static void main(String[] args) {
        // 定义 Quartz 格式的 Cron
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        // 每月的最后一个工作日凌晨 2 点
        Cron lastWorkdayOfMonth = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))
                .withMinute(FieldExpressionFactory.on(0))
                .withHour(FieldExpressionFactory.on(2))
                .withDoM(FieldExpressionFactory.lastWeekdayOfMonth())
                .withMonth(FieldExpressionFactory.every())
                .withDoW(FieldExpressionFactory.questionMark())
                .instance();

        // 每年的 1 月 1 日凌晨 0 点
        Cron newYear = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))
                .withMinute(FieldExpressionFactory.on(0))
                .withHour(FieldExpressionFactory.on(0))
                .withDoM(FieldExpressionFactory.on(1))
                .withMonth(FieldExpressionFactory.on(1))
                .withDoW(FieldExpressionFactory.questionMark())
                .instance();

        // 输出生成的 Cron 表达式
        System.out.println("每月最后一个工作日凌晨 2 点: " + lastWorkdayOfMonth.asString());
        System.out.println("每年 1 月 1 日凌晨 0 点: " + newYear.asString());
    }
}

运行结果:

每月最后一个工作日凌晨 2 点: 0 0 2 LW * ?
每年 1 月 1 日凌晨 0 点: 0 0 0 1 1 ?

6. 总结

  • 灵活性:通过 CronBuilderFieldExpressionFactory,可以灵活地定义各种复杂的调度规则。
  • 动态生成:可以根据用户输入或业务逻辑动态生成 Cron 表达式。
  • 多场景适用:支持从简单的固定时间调度到复杂的周期性任务调度。

7. Cron 表达式的含义

Cron 表达式 0 15 10 ? * 2 的字段解释如下(Quartz 格式):

字段含义值范围示例值
每分钟的第几秒触发0-590
每小时的第几分钟触发0-5915
每天的第几小时触发0-2310
每月的第几天触发1-31 或 ??
每年的第几个月触发1-12 或 **
每周的哪一天触发0-7 或 ?2

解析:

  • 0:表示每分钟的第 0 秒触发。
  • 15:表示每小时的第 15 分钟触发。
  • 10:表示每天的第 10 小时触发。
  • ?:表示“日”字段不指定(与“周”字段互斥)。
  • *:表示每月都触发。
  • 2:表示每周的星期一触发(在 Quartz 中,2 表示星期一,1 表示星期日,7 也表示星期日)。

因此,这个 Cron 表达式的含义是:

每周一上午 10 点 15 分触发任务。

  • CronUtils 的最新版本中,FieldExpressionFactory 确实没有无参数的 every() 方法。这是一个重要的细节,可能是由于库的更新或设计变更导致的。

在这种情况下,我们需要使用其他方法来实现类似“每一天”、“每一月”的功能。以下是详细的解决方案和替代方法。


1. 问题分析

(1) 为什么没有无参数的 every() 方法?

CronUtils 的设计中,字段表达式的生成是通过具体的规则来实现的。对于日期(DoM)和月份(Month)字段,“每一天”或“每一月”的逻辑可以通过以下方式实现:

  • 使用 FieldExpressionFactory.every(interval) 并指定间隔为 1。
  • 或者直接使用 FieldExpressionFactory.on("*") 表示“所有值”。

(2) 如何替代无参数的 every() 方法?

对于日期(DoM)、月份(Month)等字段,可以使用以下方法:

  • FieldExpressionFactory.on("*"):表示“所有值”,等价于无参数的 every()
  • FieldExpressionFactory.between(start, end):表示范围,例如“每月 1 到 31 日”或“每年 1 到 12 月”。

2. 解决方案

以下是针对不同字段的正确用法:

(1) 日期(DoM)字段

如果需要表示“每天”,可以使用 FieldExpressionFactory.on("*")FieldExpressionFactory.between(1, 31)

import com.cronutils.builder.CronBuilder;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpressionFactory;

public class CronExample {
    public static void main(String[] args) {
        // 定义 Quartz 格式的 Cron
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        // 动态生成 Cron 表达式
        Cron cron = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))       // 秒:0
                .withMinute(FieldExpressionFactory.every(5))    // 分:每 5 分钟
                .withHour(FieldExpressionFactory.between(9, 17)) // 时:9 点到 17 点之间
                .withDoM(FieldExpressionFactory.on("*"))        // 日:每天
                .withMonth(FieldExpressionFactory.on("*"))      // 月:每月
                .withDoW(FieldExpressionFactory.questionMark()) // 周:不指定
                .instance();

        // 输出生成的 Cron 表达式
        System.out.println("生成的 Cron 表达式: " + cron.asString());
    }
}

运行结果:

生成的 Cron 表达式: 0 0/5 9-17 * * ?

(2) 月份(Month)字段

如果需要表示“每月”,可以使用 FieldExpressionFactory.on("*")FieldExpressionFactory.between(1, 12)

.withMonth(FieldExpressionFactory.on("*")) // 每月

(3) 秒、分、时字段

对于秒、分、时字段,仍然需要使用带参数的 every(interval) 方法。

.withSecond(FieldExpressionFactory.every(10)) // 每 10 秒触发一次
.withMinute(FieldExpressionFactory.every(5))  // 每 5 分钟触发一次
.withHour(FieldExpressionFactory.every(2))    // 每 2 小时触发一次

3. 完整示例代码

以下是一个完整的动态生成 Cron 表达式的示例:

import com.cronutils.builder.CronBuilder;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.field.expression.FieldExpressionFactory;

public class DynamicCronGeneration {

    public static void main(String[] args) {
        // 定义 Quartz 格式的 Cron
        CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(CronType.QUARTZ);

        // 动态生成 Cron 表达式
        Cron cron = CronBuilder.cron(cronDefinition)
                .withSecond(FieldExpressionFactory.on(0))               // 秒:0
                .withMinute(FieldExpressionFactory.every(5))            // 分:每 5 分钟
                .withHour(FieldExpressionFactory.between(9, 17))         // 时:9 点到 17 点之间
                .withDoM(FieldExpressionFactory.on("*"))                // 日:每天
                .withMonth(FieldExpressionFactory.on("*"))              // 月:每月
                .withDoW(FieldExpressionFactory.questionMark())          // 周:不指定
                .instance();

        // 输出生成的 Cron 表达式
        System.out.println("生成的 Cron 表达式: " + cron.asString());
    }
}

运行结果:

生成的 Cron 表达式: 0 0/5 9-17 * * ?

4. 总结

  • 替代无参数的 every()
    • 使用 FieldExpressionFactory.on("*") 表示“所有值”。
    • 或使用 FieldExpressionFactory.between(start, end) 表示范围。
  • 字段适配性
    • 对于秒、分、时字段,仍然需要使用带参数的 every(interval) 方法。
    • 对于日期(DoM)和月份(Month)字段,使用 on("*")between(start, end)

如果你还有其他疑问或需要进一步优化,请随时补充说明!