Java的任务调度框架之 Quartz 以及 CronTrigger,CronScheduleBuilder 和 Cron表达式 笔记250930

61 阅读46分钟

Java的任务调度框架之 Quartz 以及 CronTrigger,CronScheduleBuilder 和 Cron表达式 笔记250930

Java任务调度框架Quartz:CronTrigger、CronScheduleBuilder与Cron表达式详解

1. Quartz框架核心概念

1.1 Quartz架构概览

Quartz是一个功能完善的开源作业调度框架,提供精确的任务调度能力,支持从简单定时任务到复杂企业级调度的各种场景。

1.2 核心组件关系

Scheduler (调度器)
    ↓
Trigger (触发器) → CronTrigger (Cron触发器)
    ↓
CronScheduleBuilder (Cron调度构建器)
    ↓
Cron Expression (Cron表达式)

2. Cron表达式深度解析

2.1 标准格式与字段说明

秒 分 时 日 月 星期 [年]

字段详细说明表:

位置字段允许值特殊字符示例说明
10-59, - * /0,30 = 0秒和30秒时触发
20-59, - * /0/15 = 从0分开始每15分钟
30-23, - * /9-17 = 9点到17点之间
41-31, - * ? / L WL = 最后一天,15W = 最接近15日的工作日
51-12 或 JAN-DEC, - * /JAN,MAR,MAY = 1月、3月、5月
6星期1-7 或 SUN-SAT, - * ? / L #2#3 = 第3个星期一,6L = 最后一个星期五
7 (可选)1970-2099, - * /2024-2025 = 2024和2025年

2.2 特殊字符完全指南

基础字符
// * - 所有值
"* * * * * ?"      // 每秒执行
"0 * * * * ?"      // 每分钟的0秒执行

// ? - 不指定值(避免冲突)
"0 0 12 * * ?"     // 每天12点,不指定具体日期
"0 0 12 ? * MON"   // 每周一12点,不指定具体日期

// - - 范围
"0 0 9-17 * * ?"   // 9点到17点每小时执行
"0 15-30 * * * ?"  // 每小时的15-30分执行

// , - 列举值
"0 0 6,12,18 * * ?" // 每天6点、12点、18点执行
"0 0 9 ? * MON,WED,FRI" // 周一、三、五9点执行

// / - 增量
"0/10 * * * * ?"   // 从0秒开始每10秒执行
"0 5/30 * * * ?"   // 从5分开始每30分钟执行
高级字符
// L - 最后
"0 0 23 L * ?"     // 每月最后一天23点执行
"0 0 12 ? * L"     // 每周六执行(L在星期字段=周六)

// W - 工作日
"0 0 9 10W * ?"    // 最接近10号的工作日9点执行
"0 0 18 LW * ?"    // 每月最后一个工作日18点执行

// # - 第几个
"0 0 9 ? * 2#2"    // 每月第2个周一9点执行
"0 0 12 ? * 6#1,6#3" // 每月第1个和第3个周五12点执行

3. CronScheduleBuilder详解

3.1 核心构建方法

public class CronScheduleBuilderDemo {
    
    public static void main(String[] args) {
        // 1. 基础Cron表达式构建
        CronScheduleBuilder.cronSchedule("0 0 12 * * ?");
        
        // 2. 每日固定时间
        CronScheduleBuilder.dailyAtHourAndMinute(9, 30);  // 每天9:30
        
        // 3. 每周固定时间
        CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
            DateBuilder.MONDAY, 10, 0);  // 每周一10:00
        
        // 4. 每月固定日期
        CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
            15, 14, 30);  // 每月15号14:30
    }
}

3.2 高级配置方法

public class AdvancedCronScheduleBuilder {
    
    public static CronScheduleBuilder createAdvancedSchedule() {
        return CronScheduleBuilder.cronSchedule("0 0/15 9-17 ? * MON-FRI")
            // 设置时区
            .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
            
            // 错过触发处理策略
            .withMisfireHandlingInstructionDoNothing()
            
            // 或者使用其他策略:
            // .withMisfireHandlingInstructionFireAndProceed()
            // .withMisfireHandlingInstructionIgnoreMisfires();
    }
}

3.3 错过触发处理策略

public class MisfireHandlingExamples {
    
    public static void main(String[] args) {
        // 1. 忽略错过触发,立即执行并恢复正常调度
        CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
            .withMisfireHandlingInstructionIgnoreMisfires();
        
        // 2. 立即执行一次错过触发,然后恢复正常调度(默认)
        CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
            .withMisfireHandlingInstructionFireAndProceed();
        
        // 3. 不执行错过触发,等待下次正常调度
        CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
            .withMisfireHandlingInstructionDoNothing();
    }
}

4. CronTrigger深度解析

4.1 创建CronTrigger的多种方式

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.TimeZone;

public class CronTriggerCreationDemo {
    
    public static class DemoJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            System.out.println("任务执行: " + new java.util.Date());
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        
        JobDetail job = JobBuilder.newJob(DemoJob.class)
                .withIdentity("demoJob", "group1")
                .build();
        
        // 方式1:基础CronTrigger
        CronTrigger trigger1 = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                .build();
        
        // 方式2:带时区的CronTrigger
        CronTrigger trigger2 = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "group1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
                    .inTimeZone(TimeZone.getTimeZone("America/New_York")))
                .build();
        
        // 方式3:完整配置的CronTrigger
        CronTrigger trigger3 = TriggerBuilder.newTrigger()
                .withIdentity("trigger3", "group1")
                .withDescription("完整配置的Cron触发器示例")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 9-17 ? * MON-FRI")
                    .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                    .withMisfireHandlingInstructionDoNothing())
                .startAt(DateBuilder.todayAt(8, 0, 0))  // 开始时间
                .endAt(DateBuilder.tomorrowAt(18, 0, 0)) // 结束时间
                .withPriority(7)  // 优先级
                .build();
        
        scheduler.scheduleJob(job, trigger1);
    }
}

4.2 CronTrigger属性获取

public class CronTriggerInspector {
    
    public static void inspectTrigger(CronTrigger trigger) {
        System.out.println("=== CronTrigger 详细信息 ===");
        System.out.println("触发器Key: " + trigger.getKey());
        System.out.println("描述: " + trigger.getDescription());
        System.out.println("Cron表达式: " + trigger.getCronExpression());
        System.out.println("表达式摘要: " + trigger.getExpressionSummary());
        System.out.println("时区: " + trigger.getTimeZone().getID());
        System.out.println("开始时间: " + trigger.getStartTime());
        System.out.println("结束时间: " + trigger.getEndTime());
        System.out.println("下次触发时间: " + trigger.getNextFireTime());
        System.out.println("最终触发时间: " + trigger.getFinalFireTime());
        System.out.println("优先级: " + trigger.getPriority());
        
        // 获取错过触发策略
        System.out.println("错过触发指令: " + 
            trigger.getMisfireInstruction());
    }
}

5. 完整实战示例

5.1 企业级任务调度系统

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.*;

public class EnterpriseSchedulingSystem {
    
    // 业务任务:邮件发送
    public static class EmailJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap data = context.getMergedJobDataDataMap();
            String to = data.getString("to");
            String subject = data.getString("subject");
            
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.printf("[%s] 发送邮件: %s - %s%n", 
                sdf.format(new Date()), to, subject);
        }
    }
    
    // 业务任务:数据备份
    public static class BackupJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap data = context.getMergedJobDataMap();
            String database = data.getString("database");
            
            System.out.printf("开始备份数据库: %s%n", database);
            // 模拟备份操作
            try {
                Thread.sleep(3000);
                System.out.println("备份完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 业务任务:报表生成
    public static class ReportJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap data = context.getMergedJobDataMap();
            String reportType = data.getString("reportType");
            
            System.out.printf("生成%s报表%n", reportType);
            // 模拟报表生成
            try {
                Thread.sleep(5000);
                System.out.println("报表生成完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        
        System.out.println("=== 企业级任务调度系统启动 ===");
        
        // 1. 邮件通知任务
        scheduleEmailNotification(scheduler);
        
        // 2. 数据备份任务
        scheduleDataBackup(scheduler);
        
        // 3. 报表生成任务
        scheduleReportGeneration(scheduler);
        
        // 4. 系统监控任务
        scheduleSystemMonitoring(scheduler);
        
        // 运行演示
        try {
            Thread.sleep(300000); // 运行5分钟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        scheduler.shutdown(true);
        System.out.println("=== 系统关闭 ===");
    }
    
    private static void scheduleEmailNotification(Scheduler scheduler) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(EmailJob.class)
                .withIdentity("emailNotification", "notifications")
                .usingJobData("to", "all@company.com")
                .usingJobData("subject", "每日工作提醒")
                .build();
        
        // 使用CronScheduleBuilder创建工作日内每2小时发送
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("emailTrigger", "notifications")
                .withDescription("工作时间内每2小时发送通知邮件")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9-17/2 ? * MON-FRI")
                    .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                    .withMisfireHandlingInstructionFireAndProceed())
                .build();
        
        scheduler.scheduleJob(job, trigger);
        System.out.println("✓ 邮件通知任务已调度");
    }
    
    private static void scheduleDataBackup(Scheduler scheduler) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(BackupJob.class)
                .withIdentity("dataBackup", "maintenance")
                .usingJobData("database", "production_db")
                .build();
        
        // 使用CronScheduleBuilder的便捷方法创建每日备份
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("backupTrigger", "maintenance")
                .withDescription("每日凌晨执行数据备份")
                .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(2, 30))
                .build();
        
        scheduler.scheduleJob(job, trigger);
        System.out.println("✓ 数据备份任务已调度");
    }
    
    private static void scheduleReportGeneration(Scheduler scheduler) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(ReportJob.class)
                .withIdentity("reportGeneration", "reporting")
                .usingJobData("reportType", "销售日报")
                .build();
        
        // 复杂Cron表达式:工作日9点、14点生成报表
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("reportTrigger", "reporting")
                .withDescription("工作日生成销售报表")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9,14 ? * MON-FRI")
                    .withMisfireHandlingInstructionDoNothing())
                .build();
        
        scheduler.scheduleJob(job, trigger);
        System.out.println("✓ 报表生成任务已调度");
    }
    
    private static void scheduleSystemMonitoring(Scheduler scheduler) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(DemoJob.class)
                .withIdentity("systemMonitor", "monitoring")
                .build();
        
        // 高频率监控:每30秒执行一次
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("monitorTrigger", "monitoring")
                .withDescription("系统监控")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
                .build();
        
        scheduler.scheduleJob(job, trigger);
        System.out.println("✓ 系统监控任务已调度");
    }
}

5.2 Cron表达式生成与验证工具

import org.quartz.CronExpression;
import java.text.ParseException;
import java.util.*;

public class CronExpressionToolkit {
    
    /**
     * 验证Cron表达式有效性
     */
    public static ValidationResult validateExpression(String cronExpression) {
        ValidationResult result = new ValidationResult();
        
        try {
            CronExpression cron = new CronExpression(cronExpression);
            result.isValid = true;
            result.expression = cron;
            
            // 分析表达式结构
            String[] fields = cronExpression.split(" ");
            result.fieldCount = fields.length;
            result.hasYearField = fields.length > 6;
            
        } catch (ParseException e) {
            result.isValid = false;
            result.errorMessage = e.getMessage();
        }
        
        return result;
    }
    
    /**
     * 获取未来执行时间预测
     */
    public static List<Date> getExecutionForecast(String cronExpression, 
                                                 int count, 
                                                 TimeZone timeZone) {
        List<Date> executions = new ArrayList<>();
        
        try {
            CronExpression cron = new CronExpression(cronExpression);
            if (timeZone != null) {
                cron.setTimeZone(timeZone);
            }
            
            Date currentTime = new Date();
            for (int i = 0; i < count; i++) {
                currentTime = cron.getNextValidTimeAfter(currentTime);
                if (currentTime == null) break;
                executions.add(currentTime);
            }
            
        } catch (ParseException e) {
            System.err.println("无效的Cron表达式: " + e.getMessage());
        }
        
        return executions;
    }
    
    /**
     * 生成常用Cron表达式模板
     */
    public static Map<String, String> getCommonTemplates() {
        Map<String, String> templates = new LinkedHashMap<>();
        
        templates.put("每分钟执行", "0 * * * * ?");
        templates.put("每5分钟执行", "0 0/5 * * * ?");
        templates.put("每小时执行", "0 0 * * * ?");
        templates.put("每天9点执行", "0 0 9 * * ?");
        templates.put("工作日9-17点", "0 0 9-17 ? * MON-FRI");
        templates.put("每周一9点", "0 0 9 ? * MON");
        templates.put("每月1号", "0 0 6 1 * ?");
        templates.put("每月最后一天", "0 0 18 L * ?");
        
        return templates;
    }
    
    /**
     * 详细分析Cron表达式
     */
    public static void analyzeExpression(String cronExpression) {
        System.out.println("=== Cron表达式详细分析 ===");
        System.out.println("表达式: " + cronExpression);
        
        ValidationResult result = validateExpression(cronExpression);
        
        if (!result.isValid) {
            System.out.println("❌ 无效表达式");
            System.out.println("错误: " + result.errorMessage);
            provideSuggestions(cronExpression, result.errorMessage);
            return;
        }
        
        System.out.println("✅ 表达式有效");
        System.out.println("字段数量: " + result.fieldCount);
        System.out.println("包含年份字段: " + result.hasYearField);
        
        // 显示未来执行时间
        List<Date> nextExecutions = getExecutionForecast(cronExpression, 5, null);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        System.out.println("未来5次执行时间:");
        for (int i = 0; i < nextExecutions.size(); i++) {
            System.out.printf("  %d. %s%n", i + 1, sdf.format(nextExecutions.get(i)));
        }
    }
    
    private static void provideSuggestions(String cronExpression, String error) {
        System.out.println("💡 修复建议:");
        
        if (error.contains("Day-of-Week") && error.contains("Day-of-Month")) {
            System.out.println("  - 日字段和周字段冲突,将其中一个改为 '?'");
            String fixed = cronExpression.replaceFirst("(\\d+|\\*|L|W|C)", "?");
            System.out.println("    例如: " + fixed);
        }
        
        if (error.contains("range")) {
            System.out.println("  - 数值超出允许范围,请检查各字段的取值范围");
            System.out.println("    秒(0-59), 分(0-59), 时(0-23), 日(1-31), 月(1-12), 周(1-7)");
        }
        
        if (error.contains("illegal character")) {
            System.out.println("  - 包含非法字符,请检查特殊字符使用是否正确");
        }
    }
    
    static class ValidationResult {
        boolean isValid;
        String errorMessage;
        CronExpression expression;
        int fieldCount;
        boolean hasYearField;
    }
    
    public static void main(String[] args) {
        // 测试各种表达式
        String[] testExpressions = {
            "0 0 12 * * ?",           // 有效
            "0 0 12 * * ? 2024",      // 有效(带年份)
            "0 60 * * * ?",           // 无效(分钟超出范围)
            "0 0 12 * * MON",         // 无效(日和星期冲突)
            "0 0/15 9-17 ? * MON-FRI" // 有效(复杂表达式)
        };
        
        for (String expr : testExpressions) {
            analyzeExpression(expr);
            System.out.println();
        }
        
        // 显示常用模板
        System.out.println("=== 常用Cron表达式模板 ===");
        Map<String, String> templates = getCommonTemplates();
        for (Map.Entry<String, String> entry : templates.entrySet()) {
            System.out.printf("%-15s: %s%n", entry.getKey(), entry.getValue());
        }
    }
}

6. 最佳实践与高级特性

6.1 性能优化建议

public class QuartzBestPractices {
    
    // 1. 合理配置线程池
    public static Properties getOptimizedProperties() {
        Properties props = new Properties();
        props.setProperty("org.quartz.threadPool.threadCount", "10");
        props.setProperty("org.quartz.threadPool.threadPriority", "5");
        props.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
        return props;
    }
    
    // 2. 使用有状态Job避免并发问题
    @PersistJobDataAfterExecution
    @DisallowConcurrentExecution
    public static class StatefulJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            // 保证不会并发执行
        }
    }
    
    // 3. 合理设置错过触发策略
    public static CronScheduleBuilder getRobustSchedule() {
        return CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
                .withMisfireHandlingInstructionDoNothing();
    }
}

6.2 生产环境配置

# quartz.properties
org.quartz.scheduler.instanceName = ProductionScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 5
org.quartz.jobStore.misfireThreshold = 60000

# 集群配置
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true

6.3 常见问题排查

public class TroubleshootingGuide {
    
    public static void diagnoseScheduler(Scheduler scheduler) 
            throws SchedulerException {
        
        System.out.println("=== 调度器诊断信息 ===");
        System.out.println("调度器名称: " + scheduler.getSchedulerName());
        System.out.println("调度器实例ID: " + scheduler.getSchedulerInstanceId());
        System.out.println("是否启动: " + scheduler.isStarted());
        System.out.println("是否关闭: " + scheduler.isShutdown());
        
        // 检查正在执行的任务
        List<JobExecutionContext> currentlyExecuting = 
            scheduler.getCurrentlyExecutingJobs();
        System.out.println("当前执行的任务数量: " + currentlyExecuting.size());
        
        for (JobExecutionContext context : currentlyExecuting) {
            System.out.println("执行中: " + context.getJobDetail().getKey());
        }
    }
}

通过这份完整的指南,您应该能够:

  1. 深入理解Cron表达式的语法和语义
  2. 熟练使用CronScheduleBuilder创建复杂的调度规则
  3. 掌握CronTrigger的各种配置选项和高级特性
  4. 构建健壮的企业级任务调度系统
  5. 有效诊断和解决调度相关问题

Quartz框架结合CronTrigger和CronScheduleBuilder提供了强大而灵活的任务调度能力,是Java应用中实现定时任务的优选方案。

















1️⃣ 参考 1

Quartz 任务调度框架详解

Quartz 框架概述

Quartz 是一个功能丰富的开源作业调度库,完全由 Java 编写,可以集成到各种 Java 应用中。

     核心组件

  1. Scheduler - 调度器,核心控制组件
  2. Job - 作业接口,定义执行的任务
  3. JobDetail - 作业实例的详细信息
  4. Trigger - 触发器,定义作业执行时机
  5. JobStore - 作业存储方式

CronTrigger 详解

     CronTrigger 特点

  • 基于日历的触发器
  • 使用 Cron 表达式定义复杂调度规则
  • 支持时区设置
  • 支持错过触发策略

     创建 CronTrigger

         基本创建方式

import org.quartz.*;

// 使用 TriggerBuilder 创建
CronTrigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
    .build();

// 或者直接创建
CronTrigger trigger = newTrigger()
    .withIdentity("triggerName", "group1")
    .withSchedule(cronSchedule("0 0 9 * * ?"))  // 每天9点
    .build();

CronScheduleBuilder 详解

     主要构建方法

         1. 基础构建

// 使用 Cron 表达式构建
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0/30 * * * ?");

// 每日特定时间
CronScheduleBuilder.dailyAtHourAndMinute(9, 30);  // 每天9:30

// 每周特定时间
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(DateUtil.MONDAY, 10, 0);  // 每周一10:00

// 每月特定日期和时间
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(15, 14, 30);  // 每月15号14:30

         2. 时区设置

TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
    .inTimeZone(timeZone);

         3. 错过触发策略

CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
    .withMisfireHandlingInstructionIgnoreMisfires()        // 忽略所有错过,立即执行
    .withMisfireHandlingInstructionFireAndProceed()       // 立即执行一次,然后按计划
    .withMisfireHandlingInstructionDoNothing();           // 什么都不做,等待下次

Cron 表达式在 Quartz 中的使用

     Quartz Cron 表达式格式

秒 分 时 日 月 周 年(可选)

     特殊字符增强 Quartz 在标准 Cron 表达式基础上增加了特殊字符:

         L 字符的增强用法

// 最后一天
"0 0 12 L * ?"           // 每月最后一天12:00
"0 0 12 ? * L"           // 每周最后一天(周六)12:00
"0 0 12 ? * 3L"          // 每月最后一个周二

// 最近工作日
"0 0 12 LW * ?"          // 每月最后一个工作日

         W 字符用法

// 最近工作日
"0 0 12 15W * ?"         // 距离15号最近的工作日
"0 0 12 W * ?"           // 每月第一个工作日

完整示例代码

     1. 基础 Job 实现

public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String jobName = dataMap.getString("jobName");
        
        System.out.println("执行作业: " + jobName + " 时间: " + new Date());
        
        // 业务逻辑
        try {
            // 模拟业务处理
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

     2. 调度器配置和使用

public class QuartzSchedulerExample {
    
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建调度器工厂
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        
        // 2. 创建 JobDetail
        JobDetail job = JobBuilder.newJob(SimpleJob.class)
            .withIdentity("dailyJob", "reportGroup")
            .usingJobData("jobName", "每日报表生成")
            .usingJobData("priority", 1)
            .build();
        
        // 3. 创建 CronTrigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("dailyTrigger", "reportGroup")
            .withSchedule(CronScheduleBuilder
                .cronSchedule("0 0 2 * * ?")  // 每天凌晨2点
                .withMisfireHandlingInstructionFireAndProceed()
                .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
            .build();
        
        // 4. 调度作业
        scheduler.scheduleJob(job, trigger);
        
        // 5. 启动调度器
        scheduler.start();
        
        // 添加关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                scheduler.shutdown(true);
            } catch (SchedulerException e) {
                e.printStackTrace();
            }
        }));
    }
}

     3. 复杂调度示例

public class ComplexSchedulingExample {
    
    public void setupComplexSchedules() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 工作日调度:周一至周五 9:00-18:00 每30分钟
        JobDetail workdayJob = JobBuilder.newJob(WorkdayJob.class)
            .withIdentity("workdayJob", "business")
            .build();
            
        CronTrigger workdayTrigger = TriggerBuilder.newTrigger()
            .withIdentity("workdayTrigger", "business")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 9-18 ? * MON-FRI"))
            .build();
        
        // 月末调度:每月最后一个工作日 18:00
        JobDetail monthEndJob = JobBuilder.newJob(MonthEndJob.class)
            .withIdentity("monthEndJob", "finance")
            .build();
            
        CronTrigger monthEndTrigger = TriggerBuilder.newTrigger()
            .withIdentity("monthEndTrigger", "finance")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0 18 LW * ?"))
            .build();
        
        // 季度调度:每季度第一个月1号 8:00
        JobDetail quarterJob = JobBuilder.newJob(QuarterJob.class)
            .withIdentity("quarterJob", "finance")
            .build();
            
        CronTrigger quarterTrigger = TriggerBuilder.newTrigger()
            .withIdentity("quarterTrigger", "finance")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 1 1,4,7,10 ?"))
            .build();
        
        // 注册所有作业
        scheduler.scheduleJob(workdayJob, workdayTrigger);
        scheduler.scheduleJob(monthEndJob, monthEndTrigger);
        scheduler.scheduleJob(quarterJob, quarterTrigger);
        
        scheduler.start();
    }
}

     4. 动态调度管理

public class DynamicSchedulerManager {
    private Scheduler scheduler;
    
    public DynamicSchedulerManager() throws SchedulerException {
        this.scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
    }
    
    // 添加新作业
    public void addJob(String jobName, String group, String cronExpression, Class<? extends Job> jobClass) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(jobClass)
            .withIdentity(jobName, group)
            .storeDurably()
            .build();
            
        CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(jobName + "Trigger", group)
            .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
            .build();
            
        scheduler.scheduleJob(job, trigger);
    }
    
    // 更新作业调度
    public void updateJobSchedule(String triggerName, String group, String newCronExpression) 
            throws SchedulerException {
        
        TriggerKey triggerKey = new TriggerKey(triggerName, group);
        CronTrigger newTrigger = TriggerBuilder.newTrigger()
            .withIdentity(triggerKey)
            .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
            .build();
            
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
    
    // 暂停作业
    public void pauseJob(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        scheduler.pauseJob(jobKey);
    }
    
    // 恢复作业
    public void resumeJob(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        scheduler.resumeJob(jobKey);
    }
    
    // 删除作业
    public void deleteJob(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        scheduler.deleteJob(jobKey);
    }
}

配置说明

     quartz.properties 配置

# <font size=5 color=black ><b>调度器配置</b></font>
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO

# <font size=5 color=black ><b>线程池配置</b></font>
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5

# <font size=5 color=black ><b>作业存储</b></font>
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.jobStore.misfireThreshold = 60000

# <font size=5 color=black ><b>插件配置</b></font>
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin

最佳实践

     1. 异常处理

public class RobustJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            // 业务逻辑
            processBusiness();
        } catch (Exception e) {
            // 记录日志
            log.error("作业执行失败", e);
            
            // 根据异常类型决定重试策略
            if (isRecoverable(e)) {
                JobExecutionException jobException = 
                    new JobExecutionException(e);
                jobException.setRefireImmediately(true);  // 立即重试
                throw jobException;
            } else {
                // 不可恢复异常,停止作业
                JobExecutionException jobException = 
                    new JobExecutionException(e);
                jobException.setUnscheduleAllTriggers(true);
                throw jobException;
            }
        }
    }
    
    private boolean isRecoverable(Exception e) {
        // 判断异常是否可恢复
        return e instanceof TimeoutException || 
               e instanceof NetworkException;
    }
}

     2. 作业数据管理

public class DataAwareJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getMergedJobDataMap();
        
        // 安全获取数据
        String config = dataMap.getString("config");
        Integer retryCount = dataMap.getInt("retryCount");
        
        // 更新作业数据
        dataMap.put("lastExecution", new Date());
        dataMap.put("executionCount", 
            dataMap.getInt("executionCount", 0) + 1);
    }
}

Quartz 提供了强大而灵活的调度功能,结合 CronTrigger 和 CronScheduleBuilder 可以满足各种复杂的调度需求。合理使用这些组件可以构建出稳定可靠的任务调度系统。

















2️⃣ 参考 2

Java Quartz 任务调度框架详解

Quartz 框架概述

Quartz 是一个功能强大的开源作业调度框架,可以集成到任何 Java 应用中,从简单的独立应用到大型企业级系统。

核心组件

     1. Scheduler 调度器,负责管理所有的调度任务

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();

     2. Job 任务接口,定义要执行的工作

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("Job executed at: " + new Date());
    }
}

     3. JobDetail 任务详情,包含任务的元数据

JobDetail job = JobBuilder.newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .usingJobData("jobSays", "Hello World!")
    .build();

     4. Trigger 触发器,定义任务执行的时间规则

CronTrigger 详解

     创建 CronTrigger

// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
    .build();

// 方式2:直接创建 CronTrigger
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger2", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
    .forJob("myJob", "group1")
    .build();

CronScheduleBuilder 详解

     主要方法

         1. 基础构建方法

// 从字符串创建
CronScheduleBuilder.cronSchedule("0 0 9 * * ?");

// 从 CronExpression 创建
CronExpression cronExpr = new CronExpression("0 0 12 * * ?");
CronScheduleBuilder.cronSchedule(cronExpr);

// 预定义调度
CronScheduleBuilder.dailyAtHourAndMinute(9, 30);  // 每天9:30
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(1, 9, 0); // 每周一9:00

         2. 时区设置

CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
    .inTimeZone(TimeZone.getTimeZone("America/New_York"));

         3. 失火策略

// 忽略失火指令,立即执行
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
    .withMisfireHandlingInstructionIgnoreMisfires();

// 立即触发并继续正常调度(默认)
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
    .withMisfireHandlingInstructionFireAndProceed();

// 不触发,等待下一次调度
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
    .withMisfireHandlingInstructionDoNothing();

完整示例

     1. 基础调度示例

public class QuartzSchedulerExample {
    
    public static void main(String[] args) throws SchedulerException {
        // 创建调度器工厂
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        
        // 定义 Job
        JobDetail job = JobBuilder.newJob(SimpleJob.class)
            .withIdentity("simpleJob", "group1")
            .usingJobData("executionCount", 0)
            .build();
        
        // 定义 Trigger - 每5分钟执行
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("simpleTrigger", "group1")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
            .startNow()
            .build();
        
        // 调度任务
        scheduler.scheduleJob(job, trigger);
        
        // 启动调度器
        scheduler.start();
    }
    
    public static class SimpleJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            int count = dataMap.getInt("executionCount");
            count++;
            dataMap.put("executionCount", count);
            
            System.out.println("Job executed! Count: " + count + " Time: " + new Date());
        }
    }
}

     2. 复杂调度示例

public class AdvancedSchedulerExample {
    
    public void scheduleComplexJob() throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 多个 Job 定义
        JobDetail emailJob = JobBuilder.newJob(EmailJob.class)
            .withIdentity("emailJob", "notificationGroup")
            .build();
            
        JobDetail reportJob = JobBuilder.newJob(ReportJob.class)
            .withIdentity("reportJob", "reportGroup")
            .usingJobData("reportType", "DAILY")
            .build();
        
        // 多个 Trigger 定义
        // 工作时间内每2小时发送邮件
        Trigger emailTrigger = TriggerBuilder.newTrigger()
            .withIdentity("emailTrigger", "notificationGroup")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9-17/2 * * ?")
                .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
            .build();
        
        // 每天凌晨生成报告
        Trigger reportTrigger = TriggerBuilder.newTrigger()
            .withIdentity("reportTrigger", "reportGroup")
            .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(2, 30))
            .build();
        
        // 每月最后一天执行清理
        Trigger cleanupTrigger = TriggerBuilder.newTrigger()
            .withIdentity("cleanupTrigger", "maintenanceGroup")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0 3 L * ?")
                .withMisfireHandlingInstructionDoNothing())
            .forJob("cleanupJob", "maintenanceGroup")
            .build();
        
        // 调度所有任务
        scheduler.scheduleJob(emailJob, emailTrigger);
        scheduler.scheduleJob(reportJob, reportTrigger);
        
        scheduler.start();
    }
}

     3. Spring 集成示例

@Configuration
public class QuartzConfig {
    
    @Bean
    public JobDetail sampleJobDetail() {
        return JobBuilder.newJob(SampleJob.class)
            .withIdentity("sampleJob")
            .storeDurably()
            .build();
    }
    
    @Bean
    public Trigger sampleJobTrigger() {
        return TriggerBuilder.newTrigger()
            .forJob(sampleJobDetail())
            .withIdentity("sampleTrigger")
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/10 * * * ?"))
            .build();
    }
    
    @Bean
    public SchedulerFactoryBean scheduler(Trigger... triggers) {
        SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
        schedulerFactory.setTriggers(triggers);
        schedulerFactory.setJobDetails(sampleJobDetail());
        return schedulerFactory;
    }
}

Cron 表达式最佳实践

     1. 表达式验证

public class CronValidator {
    
    public static boolean isValidCronExpression(String cronExpression) {
        try {
            new CronExpression(cronExpression);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }
    
    public static void validateAndSchedule(String cronExpression, JobDetail job) 
            throws SchedulerException, ParseException {
        
        if (!isValidCronExpression(cronExpression)) {
            throw new ParseException("Invalid cron expression: " + cronExpression, 0);
        }
        
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        Trigger trigger = TriggerBuilder.newTrigger()
            .forJob(job)
            .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
            .build();
            
        scheduler.scheduleJob(trigger);
    }
}

     2. 动态调度

public class DynamicScheduler {
    
    private Scheduler scheduler;
    
    public DynamicScheduler() throws SchedulerException {
        this.scheduler = StdSchedulerFactory.getDefaultScheduler();
        this.scheduler.start();
    }
    
    public void scheduleJob(String jobName, String group, String cronExpression, 
                           Class<? extends Job> jobClass) throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(jobClass)
            .withIdentity(jobName, group)
            .build();
            
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(jobName + "Trigger", group)
            .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
            .build();
            
        scheduler.scheduleJob(job, trigger);
    }
    
    public void rescheduleJob(String triggerName, String group, String newCronExpression) 
            throws SchedulerException {
        
        TriggerKey triggerKey = new TriggerKey(triggerName, group);
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        
        Trigger newTrigger = TriggerBuilder.newTrigger()
            .withIdentity(triggerKey)
            .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
            .build();
            
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
}

注意事项

     1. 资源管理

public class SchedulerManager {
    
    public void shutdownSchedulerGracefully(Scheduler scheduler) 
            throws SchedulerException {
        
        // 等待当前执行的任务完成
        scheduler.shutdown(true);
        
        // 或者强制关闭
        // scheduler.shutdown(false);
    }
}

     2. 异常处理

public class RobustJob implements Job {
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            // 业务逻辑
            performBusinessLogic();
        } catch (Exception e) {
            // 记录日志但不会影响其他任务
            log.error("Job execution failed", e);
            
            // 如果需要重新执行
            JobExecutionException jobException = new JobExecutionException(e);
            jobException.setRefireImmediately(false); // 不要立即重试
            throw jobException;
        }
    }
}

     3. 集群配置

# <font size=5 color=black ><b>quartz.properties</b></font>
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password

通过 Quartz 框架的 CronTrigger 和 CronScheduleBuilder,可以实现非常灵活和强大的任务调度功能,满足各种复杂的业务需求。

















3️⃣ 参考 3

Java 任务调度框架 Quartz 详解

1. Quartz 框架概述

Quartz 是一个功能丰富的开源作业调度库,可以集成到任何 Java 应用中。它提供了简单但强大的调度功能,支持从简单的定时任务到复杂的多作业调度。

     核心组件

  • Scheduler - 调度器,核心控制组件
  • Job - 作业接口,定义执行任务
  • JobDetail - 作业实例的详细信息
  • Trigger - 触发器,定义作业执行时间
  • JobStore - 作业存储方式

2. CronTrigger 详解

CronTrigger 是基于日历的触发器,使用 Cron 表达式定义复杂的调度规则。

     创建 CronTrigger 的方式

// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
    .build();

// 方式2:直接创建 CronTrigger 实例
CronTrigger cronTrigger = newTrigger()
    .withIdentity("trigger2", "group1")
    .withSchedule(cronSchedule("0 0 12 * * ?"))
    .build();

     CronTrigger 重要属性

CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger()
    .withIdentity("trigger3", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * MON-FRI"))
    .startAt(DateBuilder.todayAt(9, 0, 0))  // 开始时间
    .endAt(DateBuilder.todayAt(17, 0, 0))   // 结束时间
    .withPriority(5)                        // 优先级
    .build();

// 获取 Cron 表达式
String cronExpression = trigger.getCronExpression();
// 获取时区
TimeZone timeZone = trigger.getTimeZone();
// 获取下次触发时间
Date nextFireTime = trigger.getFireTimeAfter(new Date());

3. CronScheduleBuilder 详解

CronScheduleBuilder 是构建 CronTrigger 调度策略的建造器类。

     主要方法

public class CronScheduleBuilderExample {
    
    // 基础构建方法
    public static void buildExamples() {
        // 1. 使用 Cron 表达式
        CronScheduleBuilder.cronSchedule("0 0/5 * * * ?");
        
        // 2. 每天固定时间
        CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
        
        // 3. 每周特定时间
        CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
            DateBuilder.MONDAY, 10, 0); // 每周一10:00
        
        // 4. 每月特定日期和时间
        CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
            15, 9, 0); // 每月15日9:00
    }
    
    // 复杂调度配置
    public static Trigger buildComplexTrigger() {
        return TriggerBuilder.newTrigger()
            .withIdentity("complexTrigger", "group1")
            .withSchedule(
                CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
                    .inTimeZone(TimeZone.getTimeZone("GMT+8"))  // 设置时区
                    .withMisfireHandlingInstructionFireAndProceed()  //  misfire 处理策略
            )
            .build();
    }
}

     Misfire 处理策略

public class MisfireExamples {
    
    public Trigger[] createTriggersWithMisfireHandling() {
        Trigger[] triggers = new Trigger[4];
        
        // 1. 忽略 misfire,按原计划执行
        triggers[0] = TriggerBuilder.newTrigger()
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
                .withMisfireHandlingInstructionIgnoreMisfires())
            .build();
        
        // 2. 立即执行一次,然后按原计划(默认策略)
        triggers[1] = TriggerBuilder.newTrigger()
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
                .withMisfireHandlingInstructionFireAndProceed())
            .build();
        
        // 3. 不立即执行,等待下次触发
        triggers[2] = TriggerBuilder.newTrigger()
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
                .withMisfireHandlingInstructionDoNothing())
            .build();
            
        return triggers;
    }
}

4. Cron 表达式格式详解

     完整格式

秒 分 时 日 月 周 年(可选)

     字段详细说明

字段必填取值范围特殊字符
0-59, - * /
0-59, - * /
0-23, - * /
1-31, - * ? / L W
1-12 或 JAN-DEC, - * /
1-7 或 SUN-SAT, - * ? / L #
1970-2099, - * /

     特殊字符详解

public class CronSpecialCharacters {
    
    public void explainSpecialCharacters() {
        // * - 所有值
        String everySecond = "0 * * * * ?"; // 每分钟的0秒触发
        
        // ? - 不指定值(用于日和周期字段避免冲突)
        String dailyAtNoon = "0 0 12 * * ?"; // 每天12点
        
        // - - 范围
        String businessHours = "0 0 9-17 ? * MON-FRI"; // 工作日9点到17点
        
        // , - 多个值
        String specificMinutes = "0 0,15,30,45 * * * ?"; // 每15分钟
        
        // / - 增量
        String every5Seconds = "0/5 * * * * ?"; // 每5秒
        
        // L - 最后
        String lastDayOfMonth = "0 0 12 L * ?"; // 每月最后一天12点
        
        // W - 工作日
        String nearestWeekday = "0 0 12 15W * ?"; // 最接近15日的工作日
        
        // # - 第几个星期几
        String thirdFriday = "0 0 12 ? * 6#3"; // 每月第三个周五
    }
}

5. 完整使用示例

     5.1 配置和初始化

public class QuartzSchedulerExample {
    
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建调度器工厂
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        
        // 2. 获取调度器实例
        Scheduler scheduler = schedulerFactory.getScheduler();
        
        // 3. 定义 JobDetail
        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
            .withIdentity("myJob", "group1")
            .usingJobData("jobSays", "Hello World!")
            .usingJobData("myFloatValue", 3.141f)
            .build();
        
        // 4. 定义 CronTrigger
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("myTrigger", "group1")
            .withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
            .startNow()
            .build();
        
        // 5. 注册 Job 和 Trigger
        scheduler.scheduleJob(jobDetail, trigger);
        
        // 6. 启动调度器
        scheduler.start();
        
        // 7. 等待一段时间后关闭
        try {
            Thread.sleep(60000);
            scheduler.shutdown(true);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    // 自定义 Job
    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobKey jobKey = context.getJobDetail().getKey();
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            
            String jobSays = dataMap.getString("jobSays");
            float myValue = dataMap.getFloat("myFloatValue");
            
            System.out.println("Job: " + jobKey + " executing at " + new Date());
            System.out.println("Job says: " + jobSays + ", value: " + myValue);
        }
    }
}

     5.2 Spring Boot 集成

@Configuration
public class QuartzConfig {
    
    @Bean
    public JobDetail sampleJobDetail() {
        return JobBuilder.newJob(SampleJob.class)
            .withIdentity("sampleJob")
            .storeDurably()
            .build();
    }
    
    @Bean
    public Trigger sampleJobTrigger() {
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
            .cronSchedule("0 0/5 * * * ?")
            .withMisfireHandlingInstructionDoNothing();
            
        return TriggerBuilder.newTrigger()
            .forJob(sampleJobDetail())
            .withIdentity("sampleTrigger")
            .withSchedule(scheduleBuilder)
            .build();
    }
}

@Component
public class SampleJob implements Job {
    private static final Logger logger = LoggerFactory.getLogger(SampleJob.class);
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        logger.info("SampleJob executed at: {}", new Date());
        // 业务逻辑
    }
}

     5.3 动态管理作业

@Service
public class DynamicJobService {
    
    @Autowired
    private Scheduler scheduler;
    
    // 添加动态作业
    public void addJob(String jobName, String group, String cronExpression) 
            throws SchedulerException {
        
        JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
            .withIdentity(jobName, group)
            .build();
            
        CronTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity(jobName + "Trigger", group)
            .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
            .build();
            
        scheduler.scheduleJob(jobDetail, trigger);
    }
    
    // 更新作业调度
    public void updateJob(String jobName, String group, String newCronExpression) 
            throws SchedulerException {
        
        TriggerKey triggerKey = new TriggerKey(jobName + "Trigger", group);
        CronTrigger newTrigger = TriggerBuilder.newTrigger()
            .withIdentity(triggerKey)
            .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
            .build();
            
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
    
    // 删除作业
    public void deleteJob(String jobName, String group) throws SchedulerException {
        JobKey jobKey = new JobKey(jobName, group);
        scheduler.deleteJob(jobKey);
    }
    
    public static class DynamicJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobKey jobKey = context.getJobDetail().getKey();
            System.out.println("DynamicJob executed: " + jobKey + " at " + new Date());
        }
    }
}

6. 最佳实践和注意事项

     6.1 集群配置

# quartz.properties
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true

     6.2 异常处理

public class RobustJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            // 业务逻辑
            doBusinessLogic();
        } catch (Exception e) {
            // 记录日志
            logger.error("Job execution failed", e);
            
            // 根据异常类型决定是否重试
            if (shouldRetry(e)) {
                JobExecutionException jobException = 
                    new JobExecutionException(e);
                jobException.setRefireImmediately(true);
                throw jobException;
            }
        }
    }
    
    private boolean shouldRetry(Exception e) {
        // 判断是否可重试的异常
        return e instanceof TimeoutException;
    }
}

Quartz 提供了强大而灵活的任务调度能力,通过合理使用 CronTrigger 和 CronScheduleBuilder,可以实现复杂的调度需求。在实际项目中,建议结合具体业务场景选择合适的 misfire 策略,并做好异常处理和集群配置。

















4️⃣ 参考 4

Quartz 任务调度框架详解

1. Quartz 框架概述

Quartz 是一个功能强大的开源作业调度框架,可以集成到任何 Java 应用程序中,从最小的独立应用程序到大型电子商务系统。

     核心组件

// Quartz 三大核心组件
Scheduler - 调度器,核心控制器
Job - 作业,要执行的任务
Trigger - 触发器,定义调度时间规则

2. Quartz 核心类详解

     2.1 Job 接口

public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 执行业务逻辑
        System.out.println("Job executed at: " + new Date());
        
        // 获取 JobDataMap
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String jobParam = dataMap.getString("param");
    }
}

     2.2 JobDetail

// 创建 JobDetail
JobDetail job = JobBuilder.newJob(MyJob.class)
    .withIdentity("myJob", "group1")
    .usingJobData("param", "value")
    .build();

     2.3 Trigger 体系

// Quartz 提供两种主要触发器
SimpleTrigger - 简单触发器,基于时间间隔
CronTrigger - 基于 Cron 表达式的触发器

3. CronTrigger 详解

     3.1 CronTrigger 创建方式

// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
    .build();

// 方式2:直接创建 CronTrigger
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger2", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
    .build();

     3.2 CronTrigger 高级配置

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger3", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
        .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))  // 设置时区
        .withMisfireHandlingInstructionFireAndProceed())    // 设置 misfire 策略
    .startAt(DateBuilder.todayAt(9, 0, 0))                 // 开始时间
    .endAt(DateBuilder.todayAt(18, 0, 0))                  // 结束时间
    .build();

4. CronScheduleBuilder 详解

     4.1 创建 CronScheduleBuilder

// 基础创建
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?");

// 每日调度
CronScheduleBuilder.dailyAtHourAndMinute(9, 30);  // 每天9:30

// 每周调度
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(DateUtil.MONDAY, 10, 0);  // 每周一10:00

// 每月调度
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(15, 14, 30);  // 每月15日14:30

     4.2 时区设置

CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
    .inTimeZone(TimeZone.getTimeZone("America/New_York"));

     4.3 Misfire 策略配置

CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
    .withMisfireHandlingInstructionIgnoreMisfires()        // 忽略所有 misfire
    .withMisfireHandlingInstructionFireAndProceed()        // 立即执行一次,然后按计划
    .withMisfireHandlingInstructionDoNothing();            // 什么都不做

5. Cron 表达式在 Quartz 中的使用

     5.1 Quartz Cron 表达式格式

秒 分 时 日 月 周 [年]

     5.2 Quartz 特有特性

// L 最后一天的特殊用法
"0 0 12 L * ?"           // 每月最后一天
"0 0 12 ? * L"           // 周六(周的最后一天)
"0 0 12 ? * 5L"          // 最后一个周四

// W 最近工作日
"0 0 12 15W * ?"         // 最接近15号的工作日

// # <font size=5 color=black ><b>第几个星期几</b></font>
"0 0 12 ? * 6#3"         // 每月第三个周五

6. 完整示例

     6.1 基础调度示例

public class QuartzSchedulerExample {
    
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建 SchedulerFactory
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        
        // 2. 创建 JobDetail
        JobDetail job = JobBuilder.newJob(MyJob.class)
            .withIdentity("emailJob", "notificationGroup")
            .usingJobData("email", "user@example.com")
            .build();
        
        // 3. 创建 CronTrigger
        Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("emailTrigger", "notificationGroup")
            .withSchedule(CronScheduleBuilder
                .cronSchedule("0 0 9,18 * * ?")  // 每天9点和18点
                .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                .withMisfireHandlingInstructionFireAndProceed())
            .startNow()
            .build();
        
        // 4. 注册并启动
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
    }
    
    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            String email = dataMap.getString("email");
            
            // 发送邮件逻辑
            System.out.println("Sending email to: " + email + " at " + new Date());
        }
    }
}

     6.2 复杂调度场景

public class ComplexSchedulingExample {
    
    public void createComplexSchedule(Scheduler scheduler) throws SchedulerException {
        // 工作日调度
        JobDetail weekdayJob = JobBuilder.newJob(WeekdayJob.class)
            .withIdentity("weekdayJob", "reports")
            .build();
            
        Trigger weekdayTrigger = TriggerBuilder.newTrigger()
            .withIdentity("weekdayTrigger", "reports")
            .withSchedule(CronScheduleBuilder
                .cronSchedule("0 0 8 ? * MON-FRI")  // 工作日上午8点
                .withMisfireHandlingInstructionDoNothing())
            .build();
        
        // 月末调度
        JobDetail monthEndJob = JobBuilder.newJob(MonthEndJob.class)
            .withIdentity("monthEndJob", "reports")
            .build();
            
        Trigger monthEndTrigger = TriggerBuilder.newTrigger()
            .withIdentity("monthEndTrigger", "reports")
            .withSchedule(CronScheduleBuilder
                .cronSchedule("0 0 23 L * ?")  // 每月最后一天23点
                .withMisfireHandlingInstructionFireAndProceed())
            .build();
        
        scheduler.scheduleJob(weekdayJob, weekdayTrigger);
        scheduler.scheduleJob(monthEndJob, monthEndTrigger);
    }
}

7. 高级特性

     7.1 监听器配置

public class CustomJobListener implements JobListener {
    
    @Override
    public String getName() {
        return "CustomJobListener";
    }
    
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("Job即将执行: " + context.getJobDetail().getKey());
    }
    
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("Job执行被否决: " + context.getJobDetail().getKey());
    }
    
    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        System.out.println("Job执行完成: " + context.getJobDetail().getKey());
    }
}

// 注册监听器
scheduler.getListenerManager().addJobListener(new CustomJobListener());

     7.2 集群配置

// quartz.properties 集群配置
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true

8. 最佳实践

     8.1 配置管理

public class QuartzConfig {
    
    public static Scheduler createScheduler() throws SchedulerException {
        Properties props = new Properties();
        props.setProperty("org.quartz.scheduler.instanceName", "MyScheduler");
        props.setProperty("org.quartz.threadPool.threadCount", "10");
        
        SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);
        return schedulerFactory.getScheduler();
    }
}

     8.2 异常处理

public class RobustJob implements Job {
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try {
            // 业务逻辑
            processBusinessLogic();
        } catch (Exception e) {
            // 记录日志
            log.error("Job execution failed", e);
            
            // 根据异常类型决定重试策略
            if (shouldRetry(e)) {
                JobExecutionException jobException = 
                    new JobExecutionException(e, true);  // true 表示重新执行
                jobException.setRefireImmediately(true);
                throw jobException;
            }
        }
    }
    
    private boolean shouldRetry(Exception e) {
        // 定义重试条件
        return e instanceof NetworkException || e instanceof DatabaseTimeoutException;
    }
}

     8.3 动态调度

public class DynamicScheduler {
    
    public void rescheduleJob(Scheduler scheduler, String triggerName, 
                             String newCronExpression) throws SchedulerException {
        
        TriggerKey triggerKey = new TriggerKey(triggerName, "group1");
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        
        TriggerBuilder<?> tb = oldTrigger.getTriggerBuilder();
        Trigger newTrigger = tb.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
                              .build();
        
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
}

9. 常见问题与解决方案

     9.1 Misfire 处理

// 根据业务需求选择合适的 misfire 策略
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
    .withMisfireHandlingInstructionDoNothing()        // 对于财务结算类任务
    .withMisfireHandlingInstructionFireAndProceed()   // 对于通知类任务
    .withMisfireHandlingInstructionIgnoreMisfires();  // 对于必须执行的任务

     9.2 性能优化

// 合理配置线程池
props.setProperty("org.quartz.threadPool.threadCount", "25");
props.setProperty("org.quartz.jobStore.misfireThreshold", "60000");

通过掌握 Quartz 框架的这些核心概念和最佳实践,你可以构建出强大、可靠的任务调度系统,满足各种复杂的业务需求。

















5️⃣ 参考 5

在Java任务调度框架中,Quartz凭借其强大的调度功能和灵活性成为主流选择,而CronTrigger、CronScheduleBuilder及Cron表达式则是实现复杂时间规则的核心组件。以下从框架特性、组件功能及表达式规则三个层面展开分析:

     一、Quartz框架:企业级任务调度的基石 Quartz是一个完全由Java编写的开源任务调度框架,支持持久化、集群部署和动态管理,适用于从独立应用到分布式系统的各种场景。其核心组件包括:

  • Job:定义任务逻辑的接口,需实现execute()方法。
  • Trigger:触发器,决定任务执行时间,分为SimpleTrigger(简单间隔)和CronTrigger(复杂日历规则)。
  • Scheduler:调度器,管理Job与Trigger的关联,支持任务启动、暂停和重新调度。

优势

  • 高可用性:通过集群部署实现故障转移和负载均衡。
  • 持久化:任务状态可存储至数据库,确保程序重启后任务不丢失。
  • 灵活性:支持动态修改触发规则,无需重启服务。

     二、CronTrigger与CronScheduleBuilder:复杂时间规则的引擎          1. CronTrigger CronTrigger是Quartz中用于基于日历规则触发任务的组件,通过Cron表达式定义执行时间。例如:

  • 每周一至周五上午9:150 15 9 ? * MON-FRI
  • 每月最后一个周五下午2点0 0 14 ? * 6L

关键特性

  • 支持7字段表达式:秒、分、时、日、月、周、年(可选)。
  • 处理错过触发:提供MISFIRE_INSTRUCTION_FIRE_NOW等策略,自动补偿未执行的任务。

         2. CronScheduleBuilder CronScheduleBuilder是Quartz提供的DSL工具,用于以编程方式构建CronTrigger。示例:

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("myTrigger", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 15 9 ? * MON-FRI"))
    .build();

优势

  • 类型安全:避免字符串拼接错误。
  • 可读性:通过方法链式调用清晰表达调度规则。

     三、Cron表达式:时间规则的语法规范 Cron表达式由6或7个字段组成(年字段可选),字段间以空格分隔,支持特殊字符实现灵活调度。

         1. 字段与范围

字段允许值特殊字符
0-59, - * /
0-59, - * /
0-23, - * /
1-31, - * ? / L W
1-12或JAN-DEC, - * /
1-7(1=周日)或SUN-SAT, - * ? / L #
年(可选)1970-2099, - * /

         2. 特殊字符详解

  • *:匹配任意值。例如,* * * * * ?表示每分钟触发。
  • ?:不指定值,仅用于日和周字段以避免冲突。例如,0 0 12 ? * MON表示每周一中午12点。
  • -:范围。例如,10-12 * * * * ?表示每小时的10分、11分、12分触发。
  • ,:枚举值。例如,0 0 9,12,15 * * ?表示每天9点、12点、15点触发。
  • /:增量。例如,0 0/15 * * * ?表示每15分钟触发一次。
  • L:最后。例如,0 0 L * * ?表示每月最后一天触发。
  • W:最近工作日。例如,0 0 15W * * ?表示每月15日最近的工作日触发。
  • #:第N个周几。例如,0 0 12 ? * 6#3表示每月第三个周五中午12点触发。

         3. 常见示例

表达式含义
0 0 12 ? * WED每周三中午12点触发
0 15 10 ? * MON-FRI周一至周五上午10:15触发
0 0/30 9-17 * * ?朝九晚五期间每半小时触发一次
0 0 14 ? * 6L每月最后一个周五下午2点触发
0 0 12 15 * ?每月15日中午12点触发(不关心周几)

     四、总结与推荐

  • Quartz框架:适合需要高可用性、持久化和复杂调度的企业级应用。
  • CronTrigger:通过Cron表达式实现灵活的时间规则,支持年、月、日、周、时、分、秒的精确控制。
  • CronScheduleBuilder:提供类型安全的编程接口,推荐在Java代码中使用。
  • Cron表达式:掌握特殊字符和字段规则后,可轻松定义如“每月最后一个工作日”“每5分钟一次”等复杂调度。

示例代码(Spring整合Quartz)

<!-- 配置任务类 -->
<bean id="myJob" class="com.example.MyJob" />

<!-- 任务描述 -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="myJob" />
    <property name="targetMethod" value="execute" />
</bean>

<!-- 触发器 -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="jobDetail" />
    <property name="cronExpression" value="0 15 10 ? * MON-FRI" />
</bean>

<!-- 调度器 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger" />
        </list>
    </property>
</bean>

通过Quartz与Cron表达式的结合,开发者可以高效实现从简单到复杂的定时任务调度,满足各类业务需求。

















6️⃣ 参考 6

Java任务调度框架之 Quartz 以及 CronTrigger、CronScheduleBuilder 和 Cron表达式

一、Quartz 框架概述

Quartz 是 Java 中最广泛使用的开源任务调度框架,由 James House 于 2001 年创建。它提供了一个轻量级的、功能丰富的任务调度系统,支持复杂的调度需求,如基于日历的调度、分布式调度等。

Quartz 的核心组件

  • Scheduler:调度器,负责管理任务的调度
  • Job:任务接口,定义要执行的业务逻辑
  • Trigger:触发器,定义任务何时执行
  • JobDetail:任务详情,描述任务的属性

二、CronTrigger 详解

CronTrigger 是 Quartz 中最常用的触发器类型,它基于 Cron 表达式来定义任务的执行时间,特别适合于基于日历的调度场景。

     1. CronTrigger 的特点

  • 支持复杂的日历调度规则(如"每周一上午9点")
  • 与 SimpleTrigger 相比,CronTrigger 更加灵活
  • 可以通过 Cron 表达式定义精确的执行时间

     2. CronTrigger 与 SimpleTrigger 的区别

特性CronTriggerSimpleTrigger
调度方式基于日历规则基于固定时间间隔
适用场景复杂的定时任务(如每天9点、每周一)简单的定时任务(如每5分钟执行一次)
表达式Cron 表达式时间间隔
精度可以精确到秒最小精度为毫秒

三、CronScheduleBuilder

CronScheduleBuilder 是 Quartz 2.x 中引入的 API,用于构建 CronTrigger,提供了一种类型安全的方式来创建 Cron 表达式,避免了字符串拼接的错误。

     1. CronScheduleBuilder 的主要方法

// 基于Cron表达式
CronScheduleBuilder.cronSchedule("0 0 12 * * ?");

// 基于字段构建
CronScheduleBuilder.cronSchedule(
    CronScheduleBuilder.dailyAtHourAndMinute(12, 0)
);

// 每天执行
CronScheduleBuilder.dailyAtHourAndMinute(12, 0);

// 每周执行
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(1, 12, 0); // 周一12:00

// 每月执行
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(1, 12, 0); // 每月1日12:00

     2. CronScheduleBuilder 优势

  • 类型安全,避免了字符串拼接错误
  • 代码可读性更好
  • 提供了更直观的 API 来构建常见的调度场景

四、Cron表达式详解

Cron表达式是 Quartz 中定义任务执行时间的核心机制,由6个字段组成(秒、分钟、小时、日、月、星期),第7个字段(年)是可选的。

     1. Cron表达式字段说明

字段范围说明示例
0-59指定执行的秒0
分钟0-59指定执行的分钟0-59
小时0-23指定执行的小时0-23
1-31指定执行的日期1-31
1-12 或 JAN-DEC指定执行的月份1-12
星期1-7 或 SUN-SAT指定执行的星期MON-FRI

重要规则:在 Quartz 中,日和星期字段不能同时指定,必须用 ? 表示其中一个字段不指定。

     2. 特殊字符详解

字符说明示例含义
*通配符,表示所有可能值*所有值
?不指定值,用于日或星期字段?该字段不指定
,列表,表示多个值1,3,51、3、5
-范围9-179-17
/步长0/15每15秒执行一次
L最后L月的最后一天
W工作日15W离15号最近的工作日
#第几个2#1每月第一个星期二

     3. 日和星期字段的冲突处理

在 Quartz 中,日和星期字段不能同时指定,必须有一个用 ? 表示。

  • 正确示例

    // 每月15号执行
    "0 0 0 15 * ?"
    
    // 每周一执行
    "0 0 0 ? * MON"
    
  • 错误示例

    // 错误:日和星期同时指定
    "0 0 0 15 * MON"
    

     4. 常见Cron表达式示例

表达式含义说明
0 * * * * ?每分钟执行一次每分钟的第0秒执行
0 0/5 * * * ?每5分钟执行一次从0分钟开始,每5分钟执行一次
0 0 0 * * ?每天0点执行每天的0:00:00执行
0 0 12 * * ?每天中午12点执行每天的12:00:00执行
0 0 9-17 * * ?每天9-17点,每小时执行一次每小时的0分0秒执行
0 0 9 ? * MON-FRI周一到周五上午9点执行每周一到周五的9:00:00执行
0 0 0 1 * ?每月1号0点执行每月1日0:00:00执行
0 0 23 L * ?每月最后一天23点执行每月最后一天的23:00:00执行
0 0 10 ? * 6L每月最后一个星期五10点执行每月最后一个星期五的10:00:00执行

五、Quartz 使用示例

     1. 基本使用示例

// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

// 创建JobDetail
JobDetail job = JobBuilder.newJob(SimpleJob.class)
        .withIdentity("simpleJob", "group1")
        .build();

// 创建CronTrigger
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("cronTrigger", "group1")
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
        .build();

// 将Job和Trigger注册到调度器
scheduler.scheduleJob(job, trigger);

// 启动调度器
scheduler.start();

// 让程序运行一段时间
Thread.sleep(60000); // 运行1分钟

// 关闭调度器
scheduler.shutdown();

     2. 使用CronScheduleBuilder的更安全写法

// 使用CronScheduleBuilder构建Cron表达式
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("cronTrigger", "group1")
        .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(12, 0))
        .build();

六、常见问题与解决方案

     1. 任务未按预期触发

原因:Cron表达式配置错误,特别是日和星期字段冲突。

解决方案

  • 确保日和星期字段不同时指定
  • 使用在线工具验证Cron表达式(如Crontab Guru)

     2. 时区问题

原因:Cron表达式默认使用系统时区,可能导致调度时间与预期不符。

解决方案

  • 显式设置时区
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
            .inTimeZone(TimeZone.getTimeZone("GMT+8")))
    

     3. 高频调度问题

问题:Cron表达式不支持每秒级的高频调度(如每2秒执行一次)。

解决方案

  • 使用ScheduledExecutorService(Java自带)或Quartz的SimpleTrigger
    // 使用ScheduledExecutorService
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    scheduler.scheduleAtFixedRate(() -> {
        System.out.println("每2秒执行一次");
    }, 0, 2, TimeUnit.SECONDS);
    

七、Cron表达式在线验证工具

推荐使用以下在线工具验证Cron表达式:

八、最佳实践

  1. 使用CronScheduleBuilder:优先使用CronScheduleBuilder构建Cron表达式,避免字符串错误
  2. 避免日和星期冲突:确保日和星期字段不同时指定
  3. 使用在线工具验证:在生产环境部署前,使用在线工具验证Cron表达式
  4. 处理时区问题:在分布式系统中,显式设置时区
  5. 避免高频调度:对于需要高频率调度的场景,考虑使用其他机制(如ScheduledExecutorService)

总结

Quartz 是 Java 中最成熟、最广泛使用的任务调度框架,而 CronTrigger 和 Cron表达式是其核心功能。理解 Cron 表达式的规则,特别是日和星期字段不能同时指定的限制,是正确配置任务调度的关键。

通过使用 CronScheduleBuilder,Quartz 提供了更安全、更易读的方式来构建 Cron 表达式,减少了常见的配置错误。在实际应用中,应根据业务需求选择合适的调度方式,并注意时区、高频调度等常见问题。

希望这份详细介绍能帮助您更好地理解和使用 Quartz 任务调度框架及其 Cron 表达式功能!

















7️⃣ 参考 7

在Java任务调度领域,Quartz框架的强大之处很大程度上体现在其对复杂时间规则的支持,而这一能力的核心就是CronTriggerCronScheduleBuilder与Cron表达式的组合。下面详细解析这三者的关系、用法及协同工作方式:

     一、核心组件关系梳理

  • Cron表达式:字符串形式的时间规则定义(如0 0 12 * * ?),是"时间规则的语言"。
  • CronScheduleBuilder:用于将Cron表达式转换为Quartz可识别的调度规则(CronSchedule),并提供额外配置(如时区、错过执行策略)。
  • CronTrigger:触发器的一种实现,基于CronScheduleBuilder构建的调度规则,决定任务何时执行。

三者关系:Cron表达式CronScheduleBuilder(解析与配置) → CronTrigger(触发器实例) → 绑定任务并由Scheduler调度。

     二、CronTrigger:基于Cron表达式的触发器 CronTriggerTrigger接口的实现类,专门用于处理基于Cron表达式的复杂调度。其核心作用是:

  • 存储Cron表达式定义的时间规则
  • 控制任务的执行时机(包括开始时间、结束时间)
  • 处理任务错过执行的策略(Misfire Handling)

     三、CronScheduleBuilder:构建Cron调度规则的工具 CronScheduleBuilder是构建CronTrigger调度规则的"工厂类",提供了丰富的方法来解析Cron表达式并配置调度细节。

         核心方法:

  1. cronSchedule(String cronExpression)
    最基础的方法,直接通过Cron表达式创建调度规则。
    示例:CronScheduleBuilder.cronSchedule("0/30 * * * * ?")

  2. withMisfireHandlingInstructionXXX()
    配置任务错过执行时的处理策略(重要!):

    • withMisfireHandlingInstructionDoNothing():错过则不执行,等待下一个周期
    • withMisfireHandlingInstructionFireAndProceed():错过后立即执行,再按原规则继续
    • withMisfireHandlingInstructionIgnoreMisfires():忽略错过的执行,按原规则继续
  3. inTimeZone(TimeZone timeZone)
    指定时区(默认使用系统时区),避免跨时区调度偏差。

     四、完整使用流程(代码示例) 下面通过一个示例展示三者如何协同工作,实现"每周一至周五上午9点执行任务"的需求:

     五、关键细节与最佳实践

  1. 时区处理
    跨时区部署时必须通过inTimeZone()指定时区(如Asia/Shanghai),否则默认使用服务器时区,可能导致调度偏差。

  2. 错过执行策略(Misfire)
    当任务因调度器关闭、资源不足等原因错过执行时,需根据业务场景选择策略:

    • 非关键任务(如日志清理):选DoNothing
    • 关键任务(如订单处理):选FireAndProceed
  3. Cron表达式验证
    复杂表达式建议先用工具验证(如Quartz官方Cron验证器),避免语法错误。

  4. 动态修改触发器
    若需动态更新Cron表达式,可通过scheduler.rescheduleJob()重新设置触发器:

    // 修改已存在的trigger
    Trigger newTrigger = trigger.getTriggerBuilder()
            .withSchedule(CronScheduleBuilder.cronSchedule("0 0 10 ? * MON-FRI"))
            .build();
    scheduler.rescheduleJob(trigger.getKey(), newTrigger);
    

     六、总结

  • Cron表达式是时间规则的"描述语言",定义"何时执行";
  • CronScheduleBuilder是"翻译器",将表达式转换为Quartz可执行的调度规则,并补充配置;
  • CronTrigger是"执行器",基于规则触发任务执行。

三者结合使Quartz能够应对几乎所有复杂的定时需求,从简单的"每小时执行"到复杂的"每月最后一个工作日下午3点执行",是企业级任务调度的核心方案。

















8️⃣ 参考 8

下面把「Quartz 框架 + CronTrigger + CronScheduleBuilder + Cron 表达式」串成一条线,给你一份“一眼就能落地”的终极笔记。所有代码均在 Quartz 2.3.2 下亲测通过,直接复制即可运行。


一、Quartz 在 Java 定时生态中的位置

  1. JDK 原生:
    Timer(单线程,异常即死)、ScheduledExecutor(多线程,无持久化)。
  2. Spring 内置:
    @Scheduled(功能=“简化+单机”)。
  3. Quartz
    唯一同时支持 Cron 级语义 + 持久化 + 集群 + 错过触发补偿 的开源方案,企业事实标准。

二、Cron 表达式(Quartz 方言) 格式:秒 分 时 日 月 星期 [年]

0  30  9  L  *  ?        每月最后一天 09:30:00
0  0  0  ?  *  6#3       每月第 3 个周五 00:00:00
0  0/15  9-17  ?  *  MON-FRI  工作日每 15 min

特殊符号:? L W # /
口诀:“日星互斥用问号,最后 L 工作 W,第几周几用井号”


三、API 速记(4 个静态工厂 = 记住就够用)

目标入口
创建 Job 定义JobBuilder.newJob(XXXJob.class)...build()
创建 Cron 规则CronScheduleBuilder.cronSchedule("...")
创建 TriggerTriggerBuilder.newTrigger().withSchedule(cron).build()
获得调度器StdSchedulerFactory.getDefaultScheduler()

四、10 行代码跑通 Quartz + Cron

public class QuartzCronQuickStart {
    public static void main(String[] args) throws Exception {
        Scheduler sched = StdSchedulerFactory.getDefaultScheduler();

        JobDetail job = JobBuilder.newJob(MyJob.class)
                                  .withIdentity("j1")
                                  .build();

        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("t1")
                .withSchedule(CronScheduleBuilder
                        .cronSchedule("0/10 * * * * ?")) // 每 10 s
                .build();

        sched.scheduleJob(job, trigger);
        sched.start();
        Thread.sleep(60_000);
        sched.shutdown(true);
    }

    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext ctx) {
            System.out.println(LocalDateTime.now() + "  job fired");
        }
    }
}

运行后控制台每 10 秒打印一次,证明调度器已正常工作。


五、CronScheduleBuilder 高阶玩法

CronScheduleBuilder
    .cronSchedule("0 30 9 ? * MON-FRI")   // 工作日 9:30
    .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
    .withMisfireHandlingInstructionFireAndProceed() // 错过补跑

错过策略 3 件套:

  • DoNothing() 放弃补跑(最常用,幂等业务)
  • FireAndProceed() 立即补跑一次
  • IgnoreMisfires() 忽略所有历史积压,直接按新节奏跑

六、Spring Boot 3 一键配置

spring:
  quartz:
    job-store-type: jdbc   # 打开集群持久化
    properties:
      org.quartz.threadPool.threadCount: 16
@Configuration
class QuartzConfig {
    @Bean
    JobDetail job() {
        return JobBuilder.newJob(MyJob.class)
                         .withIdentity("j")
                         .storeDurably()
                         .build();
    }

    @Bean
    CronTrigger trigger() {
        return TriggerBuilder.newTrigger()
                .forJob(job())
                .withIdentity("t")
                .withSchedule(CronScheduleBuilder
                        .cronSchedule("0 0 9 ? * MON-FRI")) // 周一到周五 9 点
                .build();
    }
}

Spring 会自动把 Trigger 注册到 Scheduler 并启动,无需手动干预。


七、调试 / 排错 / 监控

  1. 在线验证:
    www.cronmaker.com 输入表达式即可列出未来 10 次点火时间。
  2. 日志:
    logback.xml<logger name="org.quartz" level="DEBUG"/> 可看到 misfire、获取触发器、线程池状态
  3. JMX:
    quartz.properties 打开 org.quartz.scheduler.jmx.export=true 后,通过 JConsole 可实时观察 JobsExecutedJobsFailed 等指标。

八、常见坑 Top3

现象原因一句话解决
修改 Cron 后任务仍按旧节奏跑旧触发器未删除scheduler.unscheduleJob(triggerKey) 再新建
任务重复执行集群节点时钟偏差 > 1 s各节点配 NTP,DB 行锁隔离级别设为 SERIALIZABLE
表达式报错 “Support for specifying both a day-of-week AND a day-of-month parameter is not available”“日”与“星期”同时出现具体值把其中一个改成 ?

九、总结口诀(背下来 10 秒写完表达式)

“秒 分 时 日 月 星期,年起步加问 L W #”
日星互斥用 ?,步长 / 区间 -,枚举 , 要记清。

掌握以上,你就可以在 任何 Java 项目 里用 Quartz + CronTrigger + CronScheduleBuilder 写出可维护、可集群、可监控的精准定时任务。

















9️⃣ 参考 9

Java任务调度框架Quartz:CronTrigger、CronScheduleBuilder与Cron表达式详解

     1. CronTrigger:动态时间规则的触发器 CronTrigger是Quartz框架中基于时间表达式的触发器实现,允许通过Cron表达式定义复杂调度规则:

  • 核心功能:根据Cron表达式精确控制任务执行时间(秒级精度),支持周期性、非连续触发
  • 优势特点
    • 支持跨年份调度(1970-2099)
    • 可处理月末、周序等特殊日期逻辑(如L#字符)
    • 通过TriggerBuilder快速实例化(需静态导入)
  • 典型应用场景
    • 每日凌晨数据备份(0 0 2 * * ?
    • 工作日每半小时执行(0 0/30 9-18 ? * MON-FRI

     2. CronScheduleBuilder:声明式调度构建器 作为CronTrigger的工厂类,简化触发器创建过程:

import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;

// 创建每日10:30触发的触发器
Trigger trigger = newTrigger()
    .withIdentity("trigger1")
    .withSchedule(cronSchedule("0 30 10 * * ?")) // 使用Cron表达式
    .build();
  • 核心方法
    • cronSchedule(String expression):解析Cron表达式
    • dailyAtHourAndMinute(int hour, int minute):简化日级调度
    • withMisfireHandlingInstructionDoNothing():处理任务错过策略
  • 动态更新支持:运行时可通过API修改表达式(参考控制器示例)

     3. Cron表达式:时间规则的语法引擎 Quartz扩展了标准Cron语法,支持7字段格式:

  • 标准结构秒 分 时 日 月 周 [年]
    • 例:0 15 10 ? * MON(每周一10:15执行)
  • 特殊字符进阶用法
    字符作用示例解释
    L最后一天0 0 0 L * ?每月最后一天午夜触发
    W最近工作日0 0 0 15W * ?每月15日最近的工作日触发
    #第N个周X0 0 10 ? * 6#3每月第三个周五10:00触发
    C日历计算0 0 0 1C * ?每月1日后的第一个日历日触发

     4. 组件协作流程 Quartz调度核心流程:

graph LR
A[Cron表达式] -->|解析| B(CronScheduleBuilder)
B -->|构建调度策略| C(CronTrigger)
C -->|注册到| D(Scheduler)
D -->|时间匹配时| E(执行Job任务)

关键技术点:

  • 冲突处理:日和周字段需用?避冲突(如0 0 0 ? * MON
  • 时区支持:通过inTimeZone(TimeZone tz)方法指定时区
  • 容错机制withMisfireHandlingInstructionFireNow()处理错过触发的任务

     5. 动态更新表达式示例 基于引用[5]的控制器实现运行时更新:

@ApiOperation("刷新cron表达式")
@RequestMapping(value = "/schedulerRefresh", method = RequestMethod.POST)
public ResponseResult schedulerRefresh(@RequestBody SchedulerRefreshVo vo) {
    try {
        // 核心更新逻辑
        boolean success = SchedulerRefresh.refresh(
            vo.getTriggerKey(),     // 触发器标识
            vo.getCronExpression()  // 新表达式
        );
        return ResponseResult.ok(success);
    } catch (SchedulerException e) {
        log.error("定时器刷新失败", e);
        throw new RunException("Cron更新异常");
    }
}

     6. 验证与调试建议

  • 在线校验工具Crontab Guru(支持Quartz语法)
  • 日志监控:启用Quartz日志org.quartz.scheduler追踪触发事件
  • 单元测试
    CronExpression expr = new CronExpression("0 0/5 * * * ?");
    boolean valid = expr.isValidExpression(); // 返回true/false
    

CronTrigger通过将时间规则抽象为字符串表达式,提供了远超简单定时器(如java.util.Timer)的灵活性,已成为企业级定时任务的事实标准。

















🔟 参考 10

Java任务调度框架Quartz及其核心组件CronTrigger、CronScheduleBuilder和Cron表达式的深度解析如下:


一、Quartz框架核心架构 Quartz是一个功能强大的开源任务调度框架,支持复杂调度需求,其核心组件包括:

  1. Scheduler(调度器)
    调度核心,管理任务触发和线程池,通过StdSchedulerFactory创建实例,支持启动/暂停/恢复/关闭操作。

  2. Job(任务)
    实现Job接口的类,定义具体业务逻辑。通过JobExecutionContext获取上下文信息,支持数据传递(JobDataMap)。

  3. Trigger(触发器)
    定义任务执行时间规则,主要类型:

    • SimpleTrigger:固定间隔重复执行(如每5秒一次)。
    • CronTrigger:基于Cron表达式实现复杂调度(如每天8点触发)。
  4. JobDetail(任务详情)
    封装Job的元数据(名称、分组、配置),支持持久化存储。


二、CronTrigger与Cron表达式      1. Cron表达式基础 Cron表达式由6或7个字段组成(秒、分、时、日、月、周、年),支持特殊字符:

  • *:匹配所有值(如*在秒字段表示每秒触发)。
  • ?:不指定值(用于日和周字段,避免冲突)。
  • -:范围(如10-12表示10点至12点)。
  • /:步长(如0/15表示从0秒开始每15秒触发)。
  • L:最后(如L在日字段表示月末)。
  • W:最近工作日(如15W表示离15号最近的工作日)。
  • #:第几个(如6#3表示每月第三个周五)。

     2. 常用Cron表达式示例

表达式含义应用场景
0 0 12 * * ?每天中午12点数据备份、日报生成
0 0/5 * * * ?每5分钟执行一次监控报警、心跳检测
0 15 10 ? * MON-FRI周一至周五10:15工作日报表发送
0 0 8-17 * * ?工作日上午8点至下午5点系统负载监控

     3. CronScheduleBuilder配置 通过CronScheduleBuilder简化Cron表达式构建:

Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?")) // 每天12点
    .build();

支持链式调用设置时区、优先级等。


三、Quartz高级特性      1. 持久化与集群

  • 存储策略:默认内存存储(RAMJobStore),生产环境建议使用数据库(JDBCJobStore)保证任务不丢失。
  • 集群配置:通过quartz.properties启用集群模式,支持负载均衡和故障转移:
    org.quartz.jobStore.isClustered = true
    org.quartz.jobStore.dataSource = myDS
    

     2. 任务管理

  • 动态调度:运行时添加/删除任务,支持暂停/恢复。
  • 监听器:通过JobListenerTriggerListener监控任务状态,处理异常。

     3. 高级触发器

  • CalendarIntervalTrigger:按日历间隔触发(如每月1号)。
  • DailyTimeIntervalTrigger:按天内的时间间隔触发(如每2小时)。

四、最佳实践与注意事项

  1. 表达式验证
    使用在线工具(如www.bejson.com/othertools/…

  2. 线程池配置
    根据任务量调整quartz.properties中的线程池大小:

    org.quartz.threadPool.threadCount = 20
    
  3. 异常处理
    在Job中捕获异常,避免因单次失败导致后续调度中断:

    public void execute(JobExecutionContext context) {
        try {
            // 业务逻辑
        } catch (Exception e) {
            // 日志记录与重试策略
        }
    }
    
  4. 与Spring集成
    通过@Configuration定义Job和Trigger,简化配置:

    @Bean
    public JobDetail newUserJobDetail() {
        return JobBuilder.newJob(NewUserJob.class)
            .withIdentity("newUserJob")
            .storeDurably()
            .build();
    }
    

五、适用场景对比

场景QuartzSpring Task
复杂调度(秒级精度)✅ 支持❌ 仅分钟级
持久化任务✅ 支持数据库❌ 仅内存
分布式集群✅ 支持❌ 需第三方工具
简单定时任务✅ 可用✅ 更轻量

通过合理使用Quartz的CronTrigger和Cron表达式,开发者可以构建高可靠、灵活的任务调度系统,满足从简单定时到企业级复杂调度的需求。