springboot环境下详解quartz框架的使用

158 阅读3分钟

本文内容:

  • 如何使用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参数,可以使用SimpleScheduleBuilderwithIntervalInHours方法,支持每秒、每分钟、每小时执行,也可以设置运行次数

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容器管理即可

  1. 首先自定义一个 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;
    }
}
  1. 再创建一个配置类,将自定义的 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;
    }
    
}
  1. 这样就可以在定时任务中使用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);
        }
    }
}

读到这里就点个赞吧,没看到就算了