Quartz
该扩展组件支持通过用户界面管理 Quartz 任务:
-
使用已有的
org.quartz.Job接口实现创建新的 Quartz 任务。 -
暂停和继续执行任务。
-
触发非活动任务的立即执行。
-
编辑任务触发器和参数。
-
删除任务。
Quartz 管理页面可以通过主菜单的 Quartz → Quartz jobs 访问。
扩展组件也提供了 Liquibase changelog 用于自动创建 Quartz 所需的数据库表。
安装
按照 扩展组件 章节的说明进行安装。
手动安装,需在 build.gradle 文件添加下列依赖:
implementation 'io.jmix.quartz:jmix-quartz-starter'
implementation 'io.jmix.quartz:jmix-quartz-flowui-starter'
|
如果项目中主数据库的 URL 是通过变量设置的(例如, 根据数据库的不同:
如果 |
使用
按照下列步骤创建并设定任务计划:
-
创建一个类,实现
org.quartz.Job接口。该类可以手动创建,或通过 Jmix 工具窗口 的 New → Advanced → Quartz Job 操作创建。package com.company.demo.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SampleJob implements Job { private static final Logger log = LoggerFactory.getLogger(SampleJob.class); @Override public void execute(JobExecutionContext context) throws JobExecutionException { log.info("Sample job is executed"); } }调度器会调用作业类的
execute()方法。在作业类中,可以使用@Autowired注入 Spring bean。Studio 在作业类内部通过代码补全提供注入支持。
当项目中至少有一个实现了 org.quartz.Job 接口的类时,Studio 会在 Jmix Tool Window 的 Jobs 部分中显示 Quartz 作业类。作业类也会显示在 Beans 部分中。
-
启动应用程序,打开 Quartz → Quartz jobs(Quartz 任务) 界面,并点击 Create(新建)。
-
在 Name(名称) 字段输入一个唯一的名称,并在 Class(类) 字段选择任务类。
Group(分组) 字段是可选的,用于在 UI 中对任务进行分组。
-
在 Triggers(触发器) 标签页为任务创建至少一个触发器。可以选择 Cron expression(Cron 表达式) 或 Simple(简易) 调度类型。前一种方式需要输入一个 Cron 表达式,例如,
0/5 * * ? * * *(每五秒)。后一种方式仅需输入任务执行的重复周期(单位为毫秒)。
保存触发器和任务之后,会根据触发器的设置立即开始调度。
如需修改任务设置,首先选中任务并点击 Pause(暂停)。保存修改后,可以点击 Resume(继续) 继续任务调度。
也可以点击 Execute now(马上执行) 按钮开始执行任一注册的任务,即便任务此时还没有触发器。该功能方便测试。
-
以编程方式配置作业和触发器
除了在 Quartz jobs 视图中创建之外,还可以将 Quartz 作业和触发器注册为 Spring bean。当需要在编程时定义调度任务时,可以用该方法。
在 @Configuration 的 Spring 配置类中声明这些 bean。
import com.company.demo.quartz.SampleJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfiguration {
@Bean
JobDetail sampleJob() {
return JobBuilder.newJob()
.ofType(SampleJob.class) (1)
.storeDurably()
.withIdentity("SampleJob") (2)
.build();
}
@Bean
Trigger sampleJobTrigger() {
return TriggerBuilder.newTrigger()
.forJob(sampleJob()) (3)
.withIdentity("SampleJobTrigger") (4)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) (5)
.build();
}
}
| 1 | 作业实现类。 |
| 2 | 作业标识符。 |
| 3 | 将触发器与作业关联。 |
| 4 | 显式指定触发器标识符。避免重新部署后创建重复的触发器。 |
| 5 | Cron 调度。 |
withIdentity() 定义作业或触发器的 Quartz 标识符。代码中创建的每个触发器都必须指定标识符。
如果触发器没有显式标识符,Quartz 会生成一个随机标识符。当应用程序使用持久化 Quartz 数据表时,在每次重新部署时都可能创建一个新的触发器,并导致多个触发器按照相同的调度执行同一个作业。
任务中的认证
调度器执行的代码未经认证,即未与任何用户关联。
如需调用需要认证的操作,例如,通过 DataManager 处理数据,可以使用 SystemAuthenticator 或 @Authenticated 注解:
public class SampleAuthenticatedJob implements Job {
private static final Logger log = LoggerFactory.getLogger(SampleAuthenticatedJob.class);
@Autowired
private DataManager dataManager;
@Authenticated
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
int usersCount = dataManager.load(User.class).all().list().size();
log.info("There are {} registered users", usersCount);
}
}
还可以使用 UnconstrainedDataManager 在未认证的上下文中处理数据。
|
任务参数
在 Job editor 视图的 Job data parameters(任务数据参数) 标签页可以为任务实例设置参数。可以像下面这样在任务类中使用参数:
public class SampleParameterizedJob implements Job {
private static final Logger log = LoggerFactory.getLogger(SampleParameterizedJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String paramStr = jobDataMap.entrySet().stream()
.map(e -> e.getKey() + " : " + e.getValue())
.collect(Collectors.joining(", ", "[", "]"));
log.info("Sample job is executed with parameters: " + paramStr);
}
}
跟踪任务执行
如需跟踪任务的执行,创建一个 bean,实现 JobListener 接口,或继承 JobListenerSupport:
package com.company.demo.quartz;
import jakarta.annotation.PostConstruct;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.listeners.JobListenerSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class JobExecutionListener extends JobListenerSupport {
private static final Logger log = LoggerFactory.getLogger(JobExecutionListener.class);
@Autowired
private Scheduler scheduler;
@Override
public String getName() {
return "SampleJobExecutionListener";
}
@PostConstruct
private void registerListener() { (1)
try {
scheduler.getListenerManager().addJobListener(this);
} catch (SchedulerException e) {
log.error("Cannot register job listener", e);
}
}
@Override
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException) { (2)
log.info("jobWasExecuted: name={}, context={}",
context.getJobDetail().getKey().getName(), context);
}
}
| 1 | bean 初始化完成后由 Spring 调用。 |
| 2 | 任务执行完成后,由 Quartz 调度器调用。 |
在 jobWasExecuted() 方法内,可以将任务的执行结果保存至日志或者数据库。