本文内容:
- 如何使用Springboot下的Quartz框架创建和删除任务
- 解决Quartz内部任务类无法被Bean装配的问题
- 向Quartz内部类传入参数并使用
1. 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2. 创建
我们想要创建一个定时任务,首先我们需要创建一个JobDetail(工作详细内容)和一个trigger(扳机/触发器),然后用一个Schedule将其组合起来。
2.1 定义工作内容:
JobDetail jobDetail = JobBuilder.newJob(ScheduledTask.class)
.withIdentity(awakeBO.getName() + currentId)
.usingJobData("equipmentList", JSON.toJSONString(awakeBO.getList()))
.build();
withIdentity 和 usingJobData的使用放到后文再讲,目前只关注任务的创建。
ScheduledTask即为定时任务的内容。
2.2 创建触发器
Trigger trigger = TriggerBuilder.newTrigger()
.startAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(awakeBO.getDate()))
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInHours(24).repeatForever())
.build();
trigger触发器常用方法有两个
-
定义开始时间 即
.startAt方法- startAt 在 Date 时间开始
- startNow 马上开始
-
定义结束时间 即
.endAt方法- endAt 结束时间
-
定义运行周期 即
.withSchedule方法- 传入一个Schedule参数,可以使用
SimpleScheduleBuilder的withIntervalInHours方法,支持每秒、每分钟、每小时执行,也可以设置运行次数
- 传入一个Schedule参数,可以使用
2.3 组合任务
try{
Scheduler scheduler = stdSchedulerFactory.getScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}catch (ObjectAlreadyExistsException objectAlreadyExistsException){
return R.error("定时任务名称不能重复");
}
scheduler.start()方法就可以开始定时任务的执行了。
2.4 JobDataMap的使用
我们想要对任务传入一些参数,怎么操作?这时候就可以运用JobDataMap进行传参。
我这里是将要进行的操作以json序列化的形式存入JobDataMap中。
扳机和任务都可以使用JobData,取出的时候将以mergeJobDataMap取出
.usingJobData("equipmentList", JSON.toJSONString(awakeBO.getList()))
ScheduledTask.class(就是那个被执行的 .class文件)
JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap();
String equipmentList = (String) mergedJobDataMap.get("equipmentList");
List<Map> equipmentMapList = JSON.parseArray(equipmentList, Map.class);
2.5 withIdentity 任务起名,关闭任务
很多时候,我们有关闭这个定时任务的需求,怎么实现呢?
可以在JobData中使用.withIdentity(awakeBO.getName()) 方法传入字符串作为任务的名字。
当我们想要停止一个任务时,通过JobKey找到该任务即可
Scheduler scheduler = stdSchedulerFactory.getScheduler();
scheduler.deleteJob(JobKey.jobKey(name));
这里的name就是Identity中输入的名字
2.6 解决 Quartz Job 中无法自动装配Bean的问题
我们传入的.class文件中是无法装配Bean的,怎么解决呢?
这一部分参考了知乎这位大神的文章 如何解决 Quartz Job 中无法注入 Spring Bean - 知乎 (zhihu.com)
出现这个问题是因为定时任务的 Job 对象实例化的过程是通过 Quartz 内部自己完成的,但是我们通过 Spring 进行注入的 Bean 却是由 Spring 容器管理的,Quartz 内部无法感知到 Spring 容器管理的 Bean,所以没有办法在创建 Job 的时候就给装配进去。
那我们只需要让Quartz内部被Spring容器管理即可
- 首先自定义一个 JobFactory,通过
AutowireCapableBeanFactory将创建好的 Job 对象交给 Spring 管理
@Configuration
public class CustomJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* Create the job instance, populating it with property values taken
* from the scheduler context, job data map and trigger data map.
*
* @param bundle
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
- 再创建一个配置类,将自定义的 JobFactory 设置到
Schedule中
@Configuration
public class QuartzConfig {
@Autowired
private CustomJobFactory customJobFactory;
@SneakyThrows
@Bean
public Scheduler scheduler(){
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 自定义 JobFactory 使得在 Quartz Job 中可以使用 @Autowired
scheduler.setJobFactory(customJobFactory);
scheduler.start();
return scheduler;
}
}
- 这样就可以在定时任务中使用Spring管理的Bean了。
@Slf4j
@Component
public class ScheduledTask extends QuartzJobBean {
@Autowired
EquipmentService equipmentService;
@Autowired
EmqxService emqxService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("任务! 执行! mynameis " + jobExecutionContext.getScheduler());
JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap();
String equipmentList = (String) mergedJobDataMap.get("equipmentList");
List<Map> equipmentMapList = JSON.parseArray(equipmentList, Map.class);
for(Map map : equipmentMapList){
Long equipmentId = Long.valueOf((Integer)map.get("equipmentId"));
Integer op = (Integer) map.get("op");
List<Equipment> list = new ArrayList<>();
Equipment equipment = equipmentService.getById(equipmentId);
if(equipment != null) list.add(equipment);
emqxService.equipmentControl(op,list);
}
}
}
读到这里就点个赞吧,没看到就算了