spring 中的动态作业调度使用 quartz 调度程序启动
Dynamic job scheduling in spring boot with quartz scheduler
我想根据 UI 用户提供的计划配置动态安排作业。当用户从 UI 保存新的计划配置时,该过程必须使用新的计划参数调用新作业。可以有 n 个这样的配置来执行相同的作业。 Spring支持job detail和trigger的实现如下
定义工作细节:
@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob().ofType(SampleJob.class)
.storeDurably()
.withIdentity("Qrtz_Job_Detail")
.withDescription("Invoke Sample Job service...")
.build();
}
定义触发器:
@Bean
public Trigger trigger(JobDetail job) {
return TriggerBuilder.newTrigger().forJob(job)
.withIdentity("Qrtz_Trigger")
.withDescription("Sample trigger")
.withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
.build();
}
如何传递作业详情参数,并根据用户提供的参数动态触发?
最简单的方法是通过扩展 SpringBeanJobFactory and @Override
createJobInstance method. Then you need to define SchedulerFactoryBean and finally your Scheduler:
进行一些配置
@Configuration
public class SchedulerConfiguration {
public class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
private final AutowireCapableBeanFactory beanFactory;
@Autowired
public AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "Bean factory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
this.beanFactory.autowireBean(jobInstance);
this.beanFactory.initializeBean(jobInstance, null);
return jobInstance;
}
}
@Bean
public SchedulerFactoryBean schedulerFactory(ApplicationContext applicationContext) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(applicationContext.getAutowireCapableBeanFactory()));
return schedulerFactoryBean;
}
@Bean
public Scheduler scheduler(ApplicationContext applicationContext) throws SchedulerException {
Scheduler scheduler = schedulerFactory(applicationContext).getScheduler();
scheduler.start();
return scheduler;
}
}
然后在应用程序的任何位置,例如在 RestController 中,您都可以访问调度程序并安排新作业:
@RestController
public class ScheduleController {
@Autowired
private Scheduler scheduler;
@GetMapping(value = "/schedule/{detail}/{desc}")
public String scheduleJob(@PathVariable(value = "detail") String detail, @PathVariable(value = "desc") String desc) throws SchedulerException {
JobDetail job = newJob(detail, desc);
return scheduler.scheduleJob(job, trigger(job)).toString();
}
private JobDetail newJob(String identity, String description) {
return JobBuilder.newJob().ofType(SimpleJob.class).storeDurably()
.withIdentity(JobKey.jobKey(identity))
.withDescription(description)
.build();
}
private SimpleTrigger trigger(JobDetail jobDetail) {
return TriggerBuilder.newTrigger().forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(), jobDetail.getKey().getGroup())
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1))
.build();
}
}
您可以通过 Scheduler
- look at the documentation
控制所有计划(暂停、停止、重新启动、删除等...)
这就是 JobDataMap 参数的用途。您可以使用这些参数将任意参数传递给您的作业和触发器。一般建议使用 String 参数值来避免各种序列化问题。 JobDataMap API 提供辅助方法,您可以使用这些方法将字符串值 JobDataMap 参数值转换为各种基本 Java 对象(Integer、Long、Double、Boolean 等)。
请注意,可以在触发器级别覆盖在 JobDetail 级别指定的 JobDataMap 参数。在 JobDetail 级别,您通常会指定通用参数 and/or 应该用于所有作业执行的默认值,然后覆盖这些默认值 and/or 在触发器级别添加新参数。
详情请参考Quartz Javadoc:
我想根据 UI 用户提供的计划配置动态安排作业。当用户从 UI 保存新的计划配置时,该过程必须使用新的计划参数调用新作业。可以有 n 个这样的配置来执行相同的作业。 Spring支持job detail和trigger的实现如下
定义工作细节:
@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob().ofType(SampleJob.class)
.storeDurably()
.withIdentity("Qrtz_Job_Detail")
.withDescription("Invoke Sample Job service...")
.build();
}
定义触发器:
@Bean
public Trigger trigger(JobDetail job) {
return TriggerBuilder.newTrigger().forJob(job)
.withIdentity("Qrtz_Trigger")
.withDescription("Sample trigger")
.withSchedule(simpleSchedule().repeatForever().withIntervalInHours(1))
.build();
}
如何传递作业详情参数,并根据用户提供的参数动态触发?
最简单的方法是通过扩展 SpringBeanJobFactory and @Override
createJobInstance method. Then you need to define SchedulerFactoryBean and finally your Scheduler:
@Configuration
public class SchedulerConfiguration {
public class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
private final AutowireCapableBeanFactory beanFactory;
@Autowired
public AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "Bean factory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
this.beanFactory.autowireBean(jobInstance);
this.beanFactory.initializeBean(jobInstance, null);
return jobInstance;
}
}
@Bean
public SchedulerFactoryBean schedulerFactory(ApplicationContext applicationContext) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(applicationContext.getAutowireCapableBeanFactory()));
return schedulerFactoryBean;
}
@Bean
public Scheduler scheduler(ApplicationContext applicationContext) throws SchedulerException {
Scheduler scheduler = schedulerFactory(applicationContext).getScheduler();
scheduler.start();
return scheduler;
}
}
然后在应用程序的任何位置,例如在 RestController 中,您都可以访问调度程序并安排新作业:
@RestController
public class ScheduleController {
@Autowired
private Scheduler scheduler;
@GetMapping(value = "/schedule/{detail}/{desc}")
public String scheduleJob(@PathVariable(value = "detail") String detail, @PathVariable(value = "desc") String desc) throws SchedulerException {
JobDetail job = newJob(detail, desc);
return scheduler.scheduleJob(job, trigger(job)).toString();
}
private JobDetail newJob(String identity, String description) {
return JobBuilder.newJob().ofType(SimpleJob.class).storeDurably()
.withIdentity(JobKey.jobKey(identity))
.withDescription(description)
.build();
}
private SimpleTrigger trigger(JobDetail jobDetail) {
return TriggerBuilder.newTrigger().forJob(jobDetail)
.withIdentity(jobDetail.getKey().getName(), jobDetail.getKey().getGroup())
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1))
.build();
}
}
您可以通过 Scheduler
- look at the documentation
这就是 JobDataMap 参数的用途。您可以使用这些参数将任意参数传递给您的作业和触发器。一般建议使用 String 参数值来避免各种序列化问题。 JobDataMap API 提供辅助方法,您可以使用这些方法将字符串值 JobDataMap 参数值转换为各种基本 Java 对象(Integer、Long、Double、Boolean 等)。
请注意,可以在触发器级别覆盖在 JobDetail 级别指定的 JobDataMap 参数。在 JobDetail 级别,您通常会指定通用参数 and/or 应该用于所有作业执行的默认值,然后覆盖这些默认值 and/or 在触发器级别添加新参数。
详情请参考Quartz Javadoc: