springboot+Quartz implementation of task scheduling sample code

  • 2021-01-18 06:28:11
  • OfStack

In the spring framework, timing tasks are implemented using @Schedule, and spring calls this method based on the rules described by the attributes of the annotation cron.

spring already implements timing tasks in a fairly straightforward way, so why use Quartz?

If you now have multiple timed tasks, the rules are different, for example:

I want the credit card to be automatically paid on the 25th of every month I want to send an anonymous greeting card to my secret admirer on April 1 every year I want to backup my love action movie learning notes to cloud disk every hour

maven rely on


<dependency>
   <groupId>org.quartz-scheduler</groupId>
   <artifactId>quartz</artifactId>
   <version>2.2.1</version>
 </dependency>
 <dependency>
   <groupId>org.quartz-scheduler</groupId>
   <artifactId>quartz-jobs</artifactId>
   <version>2.2.1</version>
 </dependency>

The following is the source code of the bootdo open source learning framework

I didn't use all the fields, but the idea is to query DB,

Encapsulate the following two objects

JobDetail is responsible for storing the data required by job Trigger Sets properties such as key for job, when the rule (cron) is enabled

When the trigger condition is met, the class is found based on the set beanClass (org.quartz.Job must be implemented), and the data in JobDetail can be fetched to execute the specific business logic


@Component
public class WelcomeJob implements Job{
  @Override
  public void execute(JobExecutionContext arg0) throws JobExecutionException {
    // Your business logic 
  }
}

Table structure


CREATE TABLE `sys_task` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `cronExpression` varchar(255) DEFAULT NULL COMMENT 'cron expression ',
 `methodName` varchar(255) DEFAULT NULL COMMENT ' The name of the method called by the task ',
 `isConcurrent` varchar(255) DEFAULT NULL COMMENT ' Whether the task has a state ',
 `description` varchar(255) DEFAULT NULL COMMENT ' Task description ',
 `updateBy` varchar(64) DEFAULT NULL COMMENT ' Update the ',
 `beanClass` varchar(255) DEFAULT NULL COMMENT ' Which class method is called when the task is executed   The package name + The name of the class ',
 `createDate` datetime DEFAULT NULL COMMENT ' Creation time ',
 `jobStatus` varchar(255) DEFAULT NULL COMMENT ' Task status ',
 `jobGroup` varchar(255) DEFAULT NULL COMMENT ' Task group ',
 `updateDate` datetime DEFAULT NULL COMMENT ' Update time ',
 `createBy` varchar(64) DEFAULT NULL COMMENT ' The creator ',
 `springBean` varchar(255) DEFAULT NULL COMMENT 'Spring bean',
 `jobName` varchar(255) DEFAULT NULL COMMENT ' The task of ',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

The configuration class


import java.io.IOException;
import java.util.Properties;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import com.txgl.common.quartz.factory.JobFactory;
@Configuration
public class QuartzConfigration {

  @Autowired
  JobFactory jobFactory;

  @Bean
  public SchedulerFactoryBean schedulerFactoryBean() {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    try {
      schedulerFactoryBean.setOverwriteExistingJobs(true);
      schedulerFactoryBean.setQuartzProperties(quartzProperties());
      schedulerFactoryBean.setJobFactory(jobFactory);
    } catch (IOException e) {
      e.printStackTrace();
    }
    return schedulerFactoryBean;
  }

  //  The specified quartz.properties
  @Bean
  public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("/config/quartz.properties"));
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
  }

  //  create schedule
  @Bean(name = "scheduler")
  public Scheduler scheduler() {
    return schedulerFactoryBean().getScheduler();
  }
}

The QuartzManager code is the key, and the task is operated on by injecting Scheduler


import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.DateBuilder;
import org.quartz.DateBuilder.IntervalUnit;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import com.bootdo.common.domain.ScheduleJob;
import com.bootdo.common.quartz.factory.*;
import com.bootdo.common.utils.SpringContextHolder;;

/**
 * 
 * 
 * @title: QuartzManager.java
 * @description:  Planned task management 
 *
 */
@Service
public class QuartzManager {
  public final Logger log = Logger.getLogger(this.getClass());
  // private SchedulerFactoryBean schedulerFactoryBean
  // =SpringContextHolder.getBean(SchedulerFactoryBean.class);
  // @Autowired
  // @Qualifier("schedulerFactoryBean")
  // private SchedulerFactoryBean schedulerFactoryBean;
  @Autowired
  private Scheduler scheduler;

  /**
   *  Add tasks 
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  
  public void addJob(ScheduleJob job) {
    try {
      //  create jobDetail Instances, bindings Job The implementation class 
      //  Indicate the job The name of the, the name of the group, and the binding job class 
      Class<? extends Job> jobClass = (Class<? extends Job>) (Class.forName(job.getBeanClass()).newInstance()
          .getClass());
      JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(job.getJobName(), job.getJobGroup())//  The task name and group constitute the task key
          .build();
      //  Define scheduling trigger rules 
      //  use cornTrigger The rules 
      Trigger trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())//  The trigger key
          .startAt(DateBuilder.futureDate(1, IntervalUnit.SECOND))
          .withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).startNow().build();
      //  Register jobs and triggers with the task schedule 
      scheduler.scheduleJob(jobDetail, trigger);
      //  Start the 
      if (!scheduler.isShutdown()) {
        scheduler.start();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
// public void addJob(ScheduleJob job) throws SchedulerException {
//   if (job == null || !ScheduleJob.STATUS_RUNNING.equals(job.getJobStatus())) {
//     return;
//   }
//
//   TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
//
//   CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//
//   //  Do not exist, create 1 a 
//
//   if (null == trigger) {
//     Class<? extends Job> clazz = ScheduleJob.CONCURRENT_IS.equals(job.getIsConcurrent())
//         ? QuartzJobFactory.class
//         : QuartzJobFactoryDisallowConcurrentExecution.class;
//
//     JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
//
//     jobDetail.getJobDataMap().put("scheduleJob", job);
//
//     CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
//
//     trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
//         .withSchedule(scheduleBuilder).build();
//
//     scheduler.scheduleJob(jobDetail, trigger);
//   } else {
//     // Trigger Existing, then update the appropriate timing Settings 
//
//     CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
//
//     //  According to the new cronExpression Expression reconstruction trigger
//
//     trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//
//     //  According to the new trigger To reset job perform 
//
//     scheduler.rescheduleJob(triggerKey, trigger);
//   }
// }

  /**
   *  Get a list of all planned tasks 
   * 
   * @return
   * @throws SchedulerException
   */
  public List<ScheduleJob> getAllJob() throws SchedulerException {
    GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
    Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
    List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
    for (JobKey jobKey : jobKeys) {
      List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
      for (Trigger trigger : triggers) {
        ScheduleJob job = new ScheduleJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        job.setDescription(" The trigger :" + trigger.getKey());
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        job.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
          CronTrigger cronTrigger = (CronTrigger) trigger;
          String cronExpression = cronTrigger.getCronExpression();
          job.setCronExpression(cronExpression);
        }
        jobList.add(job);
      }
    }
    return jobList;
  }

  /**
   *  All running job
   * 
   * @return
   * @throws SchedulerException
   */
  public List<ScheduleJob> getRunningJob() throws SchedulerException {
    List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
    List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
    for (JobExecutionContext executingJob : executingJobs) {
      ScheduleJob job = new ScheduleJob();
      JobDetail jobDetail = executingJob.getJobDetail();
      JobKey jobKey = jobDetail.getKey();
      Trigger trigger = executingJob.getTrigger();
      job.setJobName(jobKey.getName());
      job.setJobGroup(jobKey.getGroup());
      job.setDescription(" The trigger :" + trigger.getKey());
      Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
      job.setJobStatus(triggerState.name());
      if (trigger instanceof CronTrigger) {
        CronTrigger cronTrigger = (CronTrigger) trigger;
        String cronExpression = cronTrigger.getCronExpression();
        job.setCronExpression(cronExpression);
      }
      jobList.add(job);
    }
    return jobList;
  }

  /**
   *  suspended 1 a job
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.pauseJob(jobKey);
  }

  /**
   *  restore 1 a job
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.resumeJob(jobKey);
  }

  /**
   *  delete 1 a job
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.deleteJob(jobKey);

  }

  /**
   *  Executed immediately job
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.triggerJob(jobKey);
  }

  /**
   *  update job Time expression 
   * 
   * @param scheduleJob
   * @throws SchedulerException
   */
  public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {

    TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());

    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());

    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

    scheduler.rescheduleJob(triggerKey, trigger);
  }
}

service implementation


import com.bootdo.common.config.Constant;
import com.bootdo.common.dao.TaskDao;
import com.bootdo.common.domain.ScheduleJob;
import com.bootdo.common.domain.TaskDO;
import com.bootdo.common.quartz.utils.QuartzManager;
import com.bootdo.common.service.JobService;
import com.bootdo.common.utils.ScheduleJobUtils;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class JobServiceImpl implements JobService {

  @Autowired
  private TaskDao taskScheduleJobMapper;

  @Autowired
  QuartzManager quartzManager;

  @Override
  public TaskDO get(Long id) {
    return taskScheduleJobMapper.get(id);
  }

  @Override
  public List<TaskDO> list(Map<String, Object> map) {
    return taskScheduleJobMapper.list(map);
  }

  @Override
  public int count(Map<String, Object> map) {
    return taskScheduleJobMapper.count(map);
  }

  @Override
  public int save(TaskDO taskScheduleJob) {
    return taskScheduleJobMapper.save(taskScheduleJob);
  }

  @Override
  public int update(TaskDO taskScheduleJob) {
    return taskScheduleJobMapper.update(taskScheduleJob);
  }

  @Override
  public int remove(Long id) {
    try {
      TaskDO scheduleJob = get(id);
      quartzManager.deleteJob(ScheduleJobUtils.entityToData(scheduleJob));
      return taskScheduleJobMapper.remove(id);
    } catch (SchedulerException e) {
      e.printStackTrace();
      return 0;
    }

  }

  @Override
  public int batchRemove(Long[] ids) {
    for (Long id : ids) {
      try {
        TaskDO scheduleJob = get(id);
        quartzManager.deleteJob(ScheduleJobUtils.entityToData(scheduleJob));
      } catch (SchedulerException e) {
        e.printStackTrace();
        return 0;
      }
    }
    return taskScheduleJobMapper.batchRemove(ids);
  }

  @Override
  public void initSchedule() throws SchedulerException {
    //  This is where you get the task information data 
    List<TaskDO> jobList = taskScheduleJobMapper.list(new HashMap<String, Object>(16));
    for (TaskDO scheduleJob : jobList) {
      if ("1".equals(scheduleJob.getJobStatus())) {
        ScheduleJob job = ScheduleJobUtils.entityToData(scheduleJob);
        quartzManager.addJob(job);
      }

    }
  }

  @Override
  public void changeStatus(Long jobId, String cmd) throws SchedulerException {
    TaskDO scheduleJob = get(jobId);
    if (scheduleJob == null) {
      return;
    }
    if (Constant.STATUS_RUNNING_STOP.equals(cmd)) {
      quartzManager.deleteJob(ScheduleJobUtils.entityToData(scheduleJob));
      scheduleJob.setJobStatus(ScheduleJob.STATUS_NOT_RUNNING);
    } else {
      if (!Constant.STATUS_RUNNING_START.equals(cmd)) {
      } else {
        scheduleJob.setJobStatus(ScheduleJob.STATUS_RUNNING);
        quartzManager.addJob(ScheduleJobUtils.entityToData(scheduleJob));
      }
    }
    update(scheduleJob);
  }

  @Override
  public void updateCron(Long jobId) throws SchedulerException {
    TaskDO scheduleJob = get(jobId);
    if (scheduleJob == null) {
      return;
    }
    if (ScheduleJob.STATUS_RUNNING.equals(scheduleJob.getJobStatus())) {
      quartzManager.updateJobCron(ScheduleJobUtils.entityToData(scheduleJob));
    }
    update(scheduleJob);
  }
}

Start 1 listener to initialize Quartz


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.bootdo.common.quartz.utils.QuartzManager;
import com.bootdo.common.service.JobService;
@Component
@Order(value = 1)
public class ScheduleJobInitListener implements CommandLineRunner {

  @Autowired
  JobService scheduleJobService;

  @Autowired
  QuartzManager quartzManager;

  @Override
  public void run(String... arg0) throws Exception {
    try {
      scheduleJobService.initSchedule();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Related articles: