扩展Spring Boot Web应用 - 增加动态定时任务

前面添加过简单的定时任务,但这个任务启动后就会一直运行直到应用结束,如果打算不重启应用就调整任务的定时执行时间,或者暂停任务甚至动态添加新的任务呢?还是有办法做到的,下面就开始我们的扩展。

添加依赖


  org.springframework.boot
  spring-boot-starter-quartz

代码修改

  1. 启动类添加@EnableScheduling注解
  2. 配置文件添加以下配置,第一行指定使用jdbc连接数据库,第二行配置启动时自动初始化quartz相关表,第三行配置quart数据库表文件路径,由于只需要初始化一次,所以当生成相应表后后面两行注释掉。
spring.quartz.job-store-type=jdbc
#spring.quartz.jdbc.initialize-schema=always
#spring.quartz.jdbc.schema=classpath:quartz-mysql.sql

quart数据库表文件可以在quartz scheduler官网下载发布包,这里使用的是2.2.3的版本,下载解压后进入quartz-2.2.3\docs\dbTables,复制tables_mysql_innodb.sql到项目src\main\resource目录下并修改文件名称和配置一致。

  1. 数据源配置,除了在配置文件配置连接数据库的相关配置外,代码中也添加以下内容:
    @Bean
    @Primary
    public DataSourceProperties defaultDsProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @QuartzDataSource  //指示Quartz使用此数据源进行数据库操作
    public DataSource getDataSource() {
        return defaultDsProperties().initializeDataSourceBuilder().build();
    }
  1. 自定义任务类:
public class CustJob implements Job{
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //这里是固定的业务逻辑,方便演示只简单打印信息
        System.out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME) + " -- Hello, world!!");
    }
}
  1. 添加任务控制器类:
@RestController
@RequestMapping("/job")
public class QuartzJobController {

    @Autowired
    private SchedulerFactoryBean schedulerFactory;

    /**
     * 添加定时任务
     * @param jobClassName: 自定义的任务类名称,例如:CustJob,不需要包和.class后缀
     * @param jobGroupName:自定义的任务组,可随意设置
     * @param cronExpression: 定时表达式
     * @throws Exception
     */
    @PostMapping(value = "/add")
    public void addjob(@RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName,
            @RequestParam(value = "cronExpression") String cronExpression) throws Exception {

        // 启动调度器
        Scheduler scheduler = schedulerFactory.getScheduler();
        scheduler.start();
        // 构建job信息
        JobDetail jobDetail = JobBuilder.newJob(CustJob.class).withIdentity(jobClassName, jobGroupName).build();
        // 表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
                .withSchedule(scheduleBuilder).build();
        try {
            scheduler.scheduleJob(jobDetail, trigger);

        } catch (SchedulerException e) {
            System.out.println("创建定时任务失败" + e);
            throw new Exception("创建定时任务失败");
        }       
    }

    @PostMapping(value = "/reschedule")
    public void reschedule(String jobClassName, String jobGroupName, String cronExpression) throws Exception {
        try {

            Scheduler scheduler = schedulerFactory.getScheduler();
            TriggerKey triggerKey = TriggerKey.triggerKey(jobClassName, jobGroupName);
            // 表达式调度构建器         
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression)
                                                  .withMisfireHandlingInstructionFireAndProceed();
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (SchedulerException e) {
            System.out.println("更新定时任务失败" + e);
            throw new Exception("更新定时任务失败");
        }
    }
    
    
    @PostMapping(value = "/pause")
    public void pausejob(@RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
        
        schedulerFactory.getScheduler().pauseJob(JobKey.jobKey(jobClassName, jobGroupName));
    }

    @PostMapping(value = "/resume")
    public void resumejob(@RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
        
        schedulerFactory.getScheduler().resumeJob(JobKey.jobKey(jobClassName, jobGroupName));
    }   
    

    @PostMapping(value = "/delete")
    public void deletejob(@RequestParam(value = "jobClassName") String jobClassName,
            @RequestParam(value = "jobGroupName") String jobGroupName) throws Exception {
        
        schedulerFactory.getScheduler().pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
        schedulerFactory.getScheduler().unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
        schedulerFactory.getScheduler().deleteJob(JobKey.jobKey(jobClassName, jobGroupName));
    }
    
}

最后编译和启动应用,可以使用postman测试相应的接口,如果有配置swagger的话,也可以在接口文档页面中测试。
有多种动态任务的话,可以定义不同的任务类,或者修改自定义任务类并扩展Controller类接口增添一些控制参数,使得任务类支持不同的业务逻辑。

你可能感兴趣的:(扩展Spring Boot Web应用 - 增加动态定时任务)