Quartz 是一个功能强大的任务调度框架,广泛用于在 Java 应用程序中定时执行任务,同时它支持 Cron 表达式、持久化任务、集群等特性。以下是 Quartz 的详细使用教程,包括安装、基本概念、简单示例和高级功能。
1. 安装 Quartz
首先,在你的项目中添加 Quartz 依赖。对于 Maven 项目,可以在
pom.xml
中添加以下依赖:
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version></dependency>
对于 Gradle 项目,可以在
build.gradle
中添加以下依赖:
implementation 'org.quartz-scheduler:quartz:2.3.2'
2. 基本概念
Quartz 的核心概念包括:
- Scheduler:调度器,是 Quartz 的核心,负责管理和调度任务。
- Job:任务,是实际执行的工作单元。需要实现
Job
接口。 - JobDetail:定义任务的详细信息,包括任务的名称、组、以及任务的类。
- Trigger:触发器,定义任务何时执行。常用的触发器包括 SimpleTrigger 和 CronTrigger。
- JobStore:任务存储,定义任务的存储方式。常见的有 RAMJobStore(内存存储)和 JDBCJobStore(数据库存储)。
3. 简单示例
以下是一个简单的 Quartz 示例,展示如何创建和调度一个任务。
1. 定义 Job 类
importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;publicclassHelloJobimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Hello, Quartz! Current time: "+System.currentTimeMillis());}}
2. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassQuartzExample{publicstaticvoidmain(String[] args)throwsSchedulerException{// 创建 Scheduler 实例Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job =JobBuilder.newJob(HelloJob.class).withIdentity("helloJob","group1").build();// 创建一个触发器,每隔5秒执行一次Trigger trigger =TriggerBuilder.newTrigger().withIdentity("helloTrigger","group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 调度任务
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
4. 高级功能
1. 使用 CronTrigger
CronTrigger 允许使用 Cron 表达式来定义复杂的调度规则。
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassCronTriggerExample{publicstaticvoidmain(String[] args)throwsSchedulerException{Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();JobDetail job =JobBuilder.newJob(HelloJob.class).withIdentity("cronJob","group1").build();// 使用 Cron 表达式创建触发器Trigger trigger =TriggerBuilder.newTrigger().withIdentity("cronTrigger","group1").withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
2. Cron 表达式
Cron 表达式用于定义任务调度的时间规则。它由6或7个字段组成,字段之间用空格分隔。以下是每个字段的含义:
┌───────────── 秒 (0 - 59)
│ ┌───────────── 分 (0 - 59)
│ │ ┌───────────── 小时 (0 - 23)
│ │ │ ┌───────────── 日 (1 - 31)
│ │ │ │ ┌───────────── 月 (1 - 12)
│ │ │ │ │ ┌───────────── 星期几 (0 - 7) (0 和 7 都是星期日)
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *
2.1 特殊字符
*
: 表示任意值。?
: 仅在日和星期字段中使用,表示不指定值。-
: 表示范围,例如10-12
表示从10到12。,
: 表示列表值,例如1,2,3
表示1、2、3。/
: 表示增量,例如0/15
表示从0开始每15分钟。L
: 表示最后,例如L
在日字段表示月的最后一天。W
: 表示最近的工作日,例如15W
表示最接近15号的工作日。#
: 表示第几个星期几,例如2#1
表示第一个星期一。
2.2 示例
0 0 12 * * ?
: 每天中午12点执行。0 15 10 ? * *
: 每天上午10:15执行。0 15 10 * * ?
: 每天上午10:15执行。0 15 10 * * ? 2024
: 2024年每天上午10:15执行。0 * 14 * * ?
: 每天下午2点到2:59每分钟执行一次。0 0/5 14 * * ?
: 每天下午2点到2:55每5分钟执行一次。0 0/5 14,18 * * ?
: 每天下午2点到2:55每5分钟执行一次,以及每天下午6点到6:55每5分钟执行一次。0 0-5 14 * * ?
: 每天下午2点到2:05每分钟执行一次。0 10,44 14 ? 3 WED
: 每年三月的每个星期三下午2:10和2:44执行。
3. 使用 JobListener
JobListener 可以在任务执行的不同阶段进行拦截和处理。
importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.quartz.JobListener;publicclassMyJobListenerimplementsJobListener{@OverridepublicStringgetName(){return"MyJobListener";}@OverridepublicvoidjobToBeExecuted(JobExecutionContext context){//在任务即将被执行时调用,可以在任务执行前进行一些准备工作或记录日志。System.out.println("Job is about to be executed: "+ context.getJobDetail().getKey());}@OverridepublicvoidjobExecutionVetoed(JobExecutionContext context){//在任务执行被否决时调用,当某些条件满足时,可以阻止任务的执行,并在此方法中执行相应的处理逻辑。System.out.println("Job execution was vetoed: "+ context.getJobDetail().getKey());}@OverridepublicvoidjobWasExecuted(JobExecutionContext context,JobExecutionException jobException){//在任务执行完成后调用,可以在任务执行后进行一些清理工作或记录日志。如果任务执行过程中抛出异常,jobException 将包含该异常信息。System.out.println("Job was executed: "+ context.getJobDetail().getKey());if(jobException !=null){System.out.println("Job encountered an exception: "+ jobException.getMessage());}}}
在 Scheduler 中注册 JobListener:
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassJobListenerExample{publicstaticvoidmain(String[] args)throwsSchedulerException{Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();//创建任务JobDetail job =JobBuilder.newJob(HelloJob.class).withIdentity("listenerJob","group1").build();创建触发器Trigger trigger =TriggerBuilder.newTrigger().withIdentity("listenerTrigger","group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 创建并注册 JobListenerMyJobListener listener =newMyJobListener();
scheduler.getListenerManager().addJobListener(listener);
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
5. 持久化支持
Quartz 支持将任务数据持久化到数据库中,以便在系统重启后恢复任务状态。可以使用 JDBCJobStore 来实现这一点。
在
quartz.properties
文件中配置 JDBCJobStore:
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.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 5
6. 并发控制
在 Quartz 中,默认情况下,任务是并发执行的。如果需要确保同一个任务实例不被并发执行,可以实现
DisallowConcurrentExecution
接口。为了清楚地展示
DisallowConcurrentExecution
注解的作用,我们可以创建两个示例:一个使用
DisallowConcurrentExecution
注解,另一个不使用该注解。通过这两个示例,我们可以观察到任务在并发执行方面的差异。
示例 1:不使用
DisallowConcurrentExecution
在这个示例中,我们创建一个简单的任务类,它会模拟一个长时间运行的任务,并且不使用
DisallowConcurrentExecution
注解。
1. 定义 Job 类
importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;publicclassConcurrentJobimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("ConcurrentJob is executing at: "+System.currentTimeMillis());try{// 模拟长时间运行的任务Thread.sleep(5000);}catch(InterruptedException e){
e.printStackTrace();}System.out.println("ConcurrentJob finished at: "+System.currentTimeMillis());}}
2. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassConcurrentJobExample{publicstaticvoidmain(String[] args)throwsSchedulerException{Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();JobDetail job =JobBuilder.newJob(ConcurrentJob.class).withIdentity("concurrentJob","group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger =TriggerBuilder.newTrigger().withIdentity("concurrentTrigger","group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
示例 2:使用
DisallowConcurrentExecution
在这个示例中,我们创建一个类似的任务类,但这次使用
DisallowConcurrentExecution
注解,确保任务不会并发执行。
1. 定义 Job 类
importorg.quartz.DisallowConcurrentExecution;importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;@DisallowConcurrentExecutionpublicclassNonConcurrentJobimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("NonConcurrentJob is executing at: "+System.currentTimeMillis());try{// 模拟长时间运行的任务Thread.sleep(5000);}catch(InterruptedException e){
e.printStackTrace();}System.out.println("NonConcurrentJob finished at: "+System.currentTimeMillis());}}
2. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassNonConcurrentJobExample{publicstaticvoidmain(String[] args)throwsSchedulerException{Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();JobDetail job =JobBuilder.newJob(NonConcurrentJob.class).withIdentity("nonConcurrentJob","group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger =TriggerBuilder.newTrigger().withIdentity("nonConcurrentTrigger","group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
运行示例
当你运行这两个示例时,你会看到以下行为:
不使用
DisallowConcurrentExecution
的输出
ConcurrentJob is executing at: 1633017600000
ConcurrentJob is executing at: 1633017602000
ConcurrentJob finished at: 1633017605000
ConcurrentJob is executing at: 1633017604000
ConcurrentJob finished at: 1633017607000
ConcurrentJob finished at: 1633017609000
可以看到,任务是并发执行的,多个任务实例同时运行。
使用
DisallowConcurrentExecution
的输出
NonConcurrentJob is executing at: 1633017600000
NonConcurrentJob finished at: 1633017605000
NonConcurrentJob is executing at: 1633017605000
NonConcurrentJob finished at: 1633017610000
NonConcurrentJob is executing at: 1633017610000
NonConcurrentJob finished at: 1633017615000
可以看到,任务是顺序执行的,新的任务实例只有在前一个实例完成后才会开始执行。
importorg.quartz.DisallowConcurrentExecution;importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;@DisallowConcurrentExecutionpublicclassNonConcurrentJobimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Non-concurrent job is executing at: "+System.currentTimeMillis());}}
通过这两个示例,我们可以清楚地看到
DisallowConcurrentExecution
注解的作用:它确保同一个任务实例不会并发执行,而是顺序执行。这在某些需要严格控制任务执行顺序的场景中非常有用。
7. 任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
1. 异步任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
以下是一个示例,展示如何在 Quartz 中实现任务依赖管理:
示例场景
假设有三个任务:
- 任务 A:完成后触发任务 B。
- 任务 B:完成后触发任务 C。
- 任务 C:最后执行。
实现步骤
- 定义三个 Job 类。
- 配置 Scheduler 和 Job。
- 在每个 Job 中执行完成后手动调度下一个依赖的 Job。
代码示例
1. 定义 Job 类
importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;publicclassJobAimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job A executed at: "+System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try{// 创建 JobB 的 JobDetailJobDetail jobB =JobBuilder.newJob(JobB.class).withIdentity("jobB","group1").build();// 创建立即触发的 TriggerTrigger triggerB =TriggerBuilder.newTrigger().withIdentity("triggerB","group1").startNow().build();// 调度 JobB
scheduler.scheduleJob(jobB, triggerB);}catch(SchedulerException e){thrownewJobExecutionException(e);}}}publicclassJobBimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job B executed at: "+System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try{// 创建 JobC 的 JobDetailJobDetail jobC =JobBuilder.newJob(JobC.class).withIdentity("jobC","group1").build();// 创建立即触发的 TriggerTrigger triggerC =TriggerBuilder.newTrigger().withIdentity("triggerC","group1").startNow().build();// 调度 JobC
scheduler.scheduleJob(jobC, triggerC);}catch(SchedulerException e){thrownewJobExecutionException(e);}}}publicclassJobCimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job C executed at: "+System.currentTimeMillis());}}
2. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassQuartzDependencyExample{publicstaticvoidmain(String[] args)throwsSchedulerException{// 创建 Scheduler 实例Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA =JobBuilder.newJob(JobA.class).withIdentity("jobA","group1").build();// 创建立即触发的 TriggerTrigger triggerA =TriggerBuilder.newTrigger().withIdentity("triggerA","group1").startNow().build();// 调度 JobA
scheduler.start();
scheduler.scheduleJob(jobA, triggerA);}}
解释
- JobA:在执行完成后,手动调度 JobB。
- JobB:在执行完成后,手动调度 JobC。
- JobC:最后执行,没有后续任务。
通过这种方式,可以实现任务之间的依赖关系。在每个任务执行完成后,手动调度下一个依赖的任务。这样,即使 Quartz 本身不直接支持任务依赖管理,也可以通过编程方式实现这一功能。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
2. 同步任务依赖管理
在上面的示例中,任务之间的调度是异步的。每个 Job 在执行完成后,会立即调度下一个 Job,但不会等待下一个 Job 的执行完成。这种方式适用于大多数场景,但如果需要同步执行(即一个任务必须等待前一个任务执行完成后再执行),则需要一些额外的机制来确保任务的同步执行。
为了实现同步的任务依赖管理,可以使用以下方法:
- 使用 JobListener:监听每个任务的执行完成事件,并在事件触发时调度下一个任务。
- 使用共享的状态或信号量:在任务之间共享状态或使用信号量来确保任务按顺序执行。
以下是一个使用
JobListener
实现同步任务依赖管理的示例:
实现步骤
- 定义三个 Job 类。
- 实现 JobListener接口。
- 配置 Scheduler 和 Job。
- 当触发
jobWasExecuted
后调度下一个依赖的 Job。
示例代码
1. 定义 Job 类
importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;publicclassJobAimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job A executed at: "+System.currentTimeMillis());}}publicclassJobBimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job B executed at: "+System.currentTimeMillis());}}publicclassJobCimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("Job C executed at: "+System.currentTimeMillis());}}
2. 定义 JobListener
importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.quartz.JobListener;importorg.quartz.SchedulerException;publicclassDependencyJobListenerimplementsJobListener{privateScheduler scheduler;publicDependencyJobListener(Scheduler scheduler){this.scheduler = scheduler;}@OverridepublicStringgetName(){return"DependencyJobListener";}@OverridepublicvoidjobToBeExecuted(JobExecutionContext context){// No action needed before job execution}@OverridepublicvoidjobExecutionVetoed(JobExecutionContext context){// No action needed if job execution is vetoed}@OverridepublicvoidjobWasExecuted(JobExecutionContext context,JobExecutionException jobException){String jobName = context.getJobDetail().getKey().getName();try{if("jobA".equals(jobName)){// 调度 JobBJobDetail jobB =JobBuilder.newJob(JobB.class).withIdentity("jobB","group1").build();Trigger triggerB =TriggerBuilder.newTrigger().withIdentity("triggerB","group1").startNow().build();
scheduler.scheduleJob(jobB, triggerB);}elseif("jobB".equals(jobName)){// 调度 JobCJobDetail jobC =JobBuilder.newJob(JobC.class).withIdentity("jobC","group1").build();Trigger triggerC =TriggerBuilder.newTrigger().withIdentity("triggerC","group1").startNow().build();
scheduler.scheduleJob(jobC, triggerC);}}catch(SchedulerException e){
e.printStackTrace();}}}
3. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassQuartzDependencyExample{publicstaticvoidmain(String[] args)throwsSchedulerException{// 创建 Scheduler 实例Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA =JobBuilder.newJob(JobA.class).withIdentity("jobA","group1").build();// 创建立即触发的 TriggerTrigger triggerA =TriggerBuilder.newTrigger().withIdentity("triggerA","group1").startNow().build();// 添加 JobListener
scheduler.getListenerManager().addJobListener(newDependencyJobListener(scheduler));// 调度 JobA
scheduler.start();
scheduler.scheduleJob(jobA, triggerA);}}
解释
- JobA、JobB、JobC:分别定义三个独立的 Job 类。
- DependencyJobListener:实现
JobListener
接口,在每个 Job 执行完成后,根据当前执行的 Job 调度下一个 Job。 - Scheduler 配置:在 Scheduler 中注册
DependencyJobListener
,并调度初始的 JobA。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
3. 任务拦截
在 Quartz 中,可以通过实现
JobListener
来拦截任务的执行。
JobListener
提供了几个方法,可以在任务执行前、执行后以及任务被否决时进行拦截和处理。
下面是一个详细的示例代码,展示如何使用
JobListener
来拦截任务的执行,并附有详细注释。
示例代码
1. 定义 Job 类
importorg.quartz.Job;importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;// 定义一个简单的 Job 类publicclassSampleJobimplementsJob{@Overridepublicvoidexecute(JobExecutionContext context)throwsJobExecutionException{System.out.println("SampleJob is executing at: "+System.currentTimeMillis());}}
2. 实现 JobListener
importorg.quartz.JobExecutionContext;importorg.quartz.JobExecutionException;importorg.quartz.JobListener;// 实现 JobListener 接口publicclassMyJobListenerimplementsJobListener{@OverridepublicStringgetName(){// 返回监听器的名称return"MyJobListener";}@OverridepublicvoidjobToBeExecuted(JobExecutionContext context){// 在任务即将执行时调用System.out.println("Job is about to be executed: "+ context.getJobDetail().getKey());}@OverridepublicvoidjobExecutionVetoed(JobExecutionContext context){// 在任务被否决时调用System.out.println("Job execution was vetoed: "+ context.getJobDetail().getKey());}@OverridepublicvoidjobWasExecuted(JobExecutionContext context,JobExecutionException jobException){// 在任务执行完成后调用System.out.println("Job was executed: "+ context.getJobDetail().getKey());if(jobException !=null){System.out.println("Job encountered an exception: "+ jobException.getMessage());}}}
3. 配置 Scheduler 和 Job
importorg.quartz.*;importorg.quartz.impl.StdSchedulerFactory;publicclassQuartzJobListenerExample{publicstaticvoidmain(String[] args)throwsSchedulerException{// 创建 Scheduler 实例Scheduler scheduler =StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job =JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob","group1").build();// 创建一个触发器Trigger trigger =TriggerBuilder.newTrigger().withIdentity("sampleTrigger","group1").startNow().build();// 创建并注册 JobListenerMyJobListener listener =newMyJobListener();
scheduler.getListenerManager().addJobListener(listener);// 调度任务
scheduler.start();
scheduler.scheduleJob(job, trigger);}}
解释
- SampleJob:定义一个简单的 Job 类,包含一个
execute
方法,打印当前时间。 - MyJobListener:实现
JobListener
接口,提供四个方法: -getName
:返回监听器的名称。-jobToBeExecuted
:在任务即将执行时调用,打印任务的 Key。-jobExecutionVetoed
:在任务被否决时调用,打印任务的 Key。-jobWasExecuted
:在任务执行完成后调用,打印任务的 Key 和异常信息(如果有)。 - QuartzJobListenerExample:主类,配置 Scheduler 和 Job: - 创建 Scheduler 实例。- 定义一个
JobDetail
实例。- 创建一个触发器。- 创建并注册JobListener
实例。- 调度任务并启动 Scheduler。
注意事项
- 异常处理:在
jobWasExecuted
方法中处理任务执行中的异常,确保异常信息被记录或处理。 - 并发控制:如果有多个任务并发执行,需要确保监听器的实现是线程安全的。
- 持久化支持:在需要持久化任务状态时,确保 Scheduler 配置支持持久化。
版权归原作者 Vesper63 所有, 如有侵权,请联系我们删除。