Spring boot integrates quartz 2.0 to realize two ways of front-end dynamic configuration (obtaining spring context), starting the scheduled tasks that have been opened in the database

We can get the spring context directly in the custom job class. In general, when we integrate the spring injection class, we will only get a null pointer exception, saying that the bean is not injected, let's see the effect first.

The first acquisition:

import com.len.util.SpringUtil;
import com.len.entity.SysUser;
import com.len.service.SysUserService;
import java.text.SimpleDateFormat;
import java.util.List;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;

/**
 * @author zhuxiaomeng
 * @date 2018/1/7.
 * @email [email protected]
 *
 * 定时
 */
public class JobDemo1 implements Job{

  @Autowired
  SysUserService sys;

  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    System.out.println("JobDemo1:启动任务=======================");
    run();
    System.out.println("JobDemo1:下次执行时间====="+
        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
            .format(context.getNextFireTime())+"==============");
  }

  public void run(){
    ApplicationContext applicationContext=SpringUtil.getApplicationContext();
    List<SysUser> userList=sys.selectListByPage(new SysUser());
    System.out.println(userList.get(0).getUsername());;
    System.out.println("JobDemo1:执行完毕=======================");

  }
}

The second kind of acquisition

import com.len.entity.SysUser;
import com.len.service.SysUserService;
import com.len.service.impl.SysUserServiceImpl;
import com.len.util.SpringUtil;
import java.text.SimpleDateFormat;
import java.util.List;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;

/**
 * @author zhuxiaomeng
 * @date 2018/1/7.
 * @email [email protected]
 *
 * 定时测试类
 */
public class JobDemo2 implements Job{


  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    System.out.println("JobDemo2:启动任务=======================");
    run();
    System.out.println("JobDemo2:下次执行时间====="+
        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss")
            .format(context.getNextFireTime())+"==============");
  }

  public void run(){
    ApplicationContext applicationContext=SpringUtil.getApplicationContext();
    SysUserService sys=SpringUtil.getBean(SysUserServiceImpl.class);
    List<SysUser> userList=sys.selectListByPage(new SysUser());
    System.out.println(userList.get(0).getUsername());;
    System.out.println("JobDemo2:执行完毕=======================");

  }
}

Generally, we are used to using the first method to obtain bean objects: @Autowired acquisition, and the second method is to obtain the bean in the method, and the front end shows the effect:

Version used for this tutorial:

quartz:2.3.0

spring :1.5.9 RELEASE

Let's start with quartz2.x. I believe this tutorial will help you. The difference between quartz 1.x and 2.x is that the objects that need new are replaced by static factory implementations.

Quartz and spring boot integration (actually injected into spring)

Then all we have to do is to be familiar with quartz and spring's support classes for quartz

First familiarize yourself with the following main categories 

SchedulerFactoryBean: This is the scheduling factory bean provided by spring, through which we can create and configure a scheduler, manage its life cycle as part of the Spring context, and expose the scheduler's bean reference dependency injection. We can use this method to get the schduler.

Scheduler:  Quartz's main API interface and job scheduler all our JobDetail and Trigger are maintained through this class, that is, timing needs to be implemented through this class.

Job and JobDetail: Our custom task classes must implement the Job interface, in which the method execute() provides a timed entry, and the JobDetail interface provides the specific attributes required by the job class.

JobDetail jobDetail = JobBuilder.newJob(clazz).build();

Create a JobBuilder through JobBuilder and pass in a custom task class clazz, build returns a JobDetail Let's take a look at the specific operations:

public JobDetail build() {
    JobDetailImpl job = new JobDetailImpl();
    job.setJobClass(this.jobClass);
    job.setDescription(this.description);
    if(this.key == null) {
      this.key = new JobKey(Key.createUniqueName((String)null), (String)null);
    }

    job.setKey(this.key);
    job.setDurability(this.durability);
    job.setRequestsRecovery(this.shouldRecover);
    if(!this.jobDataMap.isEmpty()) {
      job.setJobDataMap(this.jobDataMap);
    }

    return job;
  }

The JobDetail interface implementation class JobDetailImpl sets various information into the JobBuilder, and finally returns to the object jobDetail .

 

Trigger : Task trigger basic interface.

CronTrigger: Inherit the Trigger interface, look at the following code:

CronTrigger trigger = TriggerBuilder.newTrigger()
          .withIdentity(triggerKey)
          .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())).build();

First, newTrigger() creates a TriggerBuilder and then passes in the trigger key and then the defined cron expression. Finally, build() returns the object trigger.

At this point we look back at the  Scheduler  interface 

scheduler.scheduleJob(jobDetail, trigger);

Through this method, the tasks we define can be arranged into the execution plan.

scheduler.start();

Then start the task. If you are interested, you can look at the specific implementation of the start method, which is ultimately the thread of operation.

Without further ado, start integrating:

Spring provides us with a class:  AdaptableJobFactory  This class implements JobFactory

Its method createJobInstance() can create a job instance and return it; by overriding its method createJobInstance(); we can get the job instance,

Fill in the spring bean through the method autowireBean of the spring interface  AutowireCapableBeanFactory 

Look at the code:

@Component
public class MyJobFactory extends AdaptableJobFactory{
      
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
  
    @Override  
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object job = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(job);
        return job;
    }
}

This method completes spring's support for quartz

Of course, we also need to inject SchedulerFactoryBean into spring:

@Configuration
public class MySchedulerListener {
      
    @Autowired  
    MyJobFactory myJobFactory;

      
    @Bean(name ="schedulerFactoryBean")
    public SchedulerFactoryBean mySchedulerFactory() {
        SchedulerFactoryBean bean = new SchedulerFactoryBean();
        bean.setJobFactory(myJobFactory);  
        return bean;  
    }  
  
}  

At this point, the custom job already supports the spring context.

Then we start to define tasks to add, delete and modify the job class:

import com.len.core.annotation.Log;
import com.len.core.annotation.Log.LOG_TYPE;
import com.len.entity.SysJob;
import java.util.Date;
import java.util.HashSet;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

/**
 * @author zhuxiaomeng
 * @date 2018/1/5.
 * @email [email protected]
 *
 * 定时任务类 增删改 可参考api:http://www.quartz-scheduler.org/api/2.2.1/
 *
 * 任务名称 默认为 SysJob 类 id
 */
@Service
public class JobTask {

  private static final Logger log = LoggerFactory.getLogger(JobTask.class);

  @Autowired
  SchedulerFactoryBean schedulerFactoryBean;

  /**
   * true 存在 false 不存在
   * @param
   * @return
   */
  public boolean checkJob(SysJob job){
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    TriggerKey triggerKey = TriggerKey.triggerKey(job.getId(), Scheduler.DEFAULT_GROUP);
    try {
      if(scheduler.checkExists(triggerKey)){
        return true;
      }
    } catch (SchedulerException e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * 开启
   */
  //@Log(desc = "开启定时任务")
  public boolean startJob(SysJob job) {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    try {
      Class clazz = Class.forName(job.getClazzPath());
      JobDetail jobDetail = JobBuilder.newJob(clazz).build();
      // 触发器
      TriggerKey triggerKey = TriggerKey.triggerKey(job.getId(), Scheduler.DEFAULT_GROUP);
      CronTrigger trigger = TriggerBuilder.newTrigger()
          .withIdentity(triggerKey)
          .withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())).build();
      scheduler.scheduleJob(jobDetail, trigger);
      // 启动
      if (!scheduler.isShutdown()) {
        scheduler.start();
        log.info("---任务[" + triggerKey.getName() + "]启动成功-------");
        return true;
      }else{
        log.info("---任务[" + triggerKey.getName() + "]已经运行,请勿再次启动-------");
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    return false;
  }

  /**
   * 更新
   */
  @Log(desc = "更新定时任务", type = LOG_TYPE.UPDATE)
  public boolean updateJob(SysJob job) {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    String createTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");

    TriggerKey triggerKey = TriggerKey.triggerKey(job.getId(), Scheduler.DEFAULT_GROUP);
    try {
      if (scheduler.checkExists(triggerKey)) {
        return false;
      }

      JobKey jobKey = JobKey.jobKey(job.getId(), Scheduler.DEFAULT_GROUP);

      CronScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule(job.getCron())
          .withMisfireHandlingInstructionDoNothing();
      CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
          .withDescription(createTime).withSchedule(schedBuilder).build();
      Class clazz = null;
      JobDetail jobDetail = scheduler.getJobDetail(jobKey);
      HashSet<Trigger> triggerSet = new HashSet<>();
      triggerSet.add(trigger);
      scheduler.scheduleJob(jobDetail, triggerSet, true);
      log.info("---任务["+triggerKey.getName()+"]更新成功-------");
      return true;
    } catch (SchedulerException e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * 删除
   */
  @Log(desc = "删除定时任务", type = LOG_TYPE.DEL)
  public boolean remove(SysJob job) {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    TriggerKey triggerKey = TriggerKey.triggerKey(job.getId(), Scheduler.DEFAULT_GROUP);
    try {
      if (checkJob(job)) {
        scheduler.pauseTrigger(triggerKey);
        scheduler.unscheduleJob(triggerKey);
        scheduler.deleteJob(JobKey.jobKey(job.getId(), Scheduler.DEFAULT_GROUP));
        log.info("---任务[" + triggerKey.getName() + "]删除成功-------");
        return true;
      }
    } catch (SchedulerException e) {
      e.printStackTrace();
    }
    return false;
  }
}

The job is already defined at the beginning,

We need to customize the startup job and store it in the database, we need an entity class:

public class SysJob implements Serializable {
    private String id;

    private String jobName;

    private String cron;

    private Boolean status;

    private String clazzPath;

    private String jobDesc;

    private String createBy;

    private Date createDate;

    private String updateBy;

    private Date updateDate;

    private static final long serialVersionUID = 1L;
}

The second way is   to obtain the context by implementing ApplicationContextAware and encapsulate it:

public class ApplicationContextUtil implements ApplicationContextAware {

  private static ApplicationContext applicationContext;
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    ApplicationContextUtil.applicationContext=applicationContext;
  }

  public static ApplicationContext getContext(){
    return applicationContext;
  }

  public static Object getBean(String arg){
    return applicationContext.getBean(arg);
  }
}

If you don't know the bean name, you can get it after the spring bootmain method starts:

public static void main(String[] args) {
    ApplicationContext applicationContext=SpringApplication.run(Application.class,args);
    String[] names = applicationContext.getBeanDefinitionNames();
    Arrays.asList(names).forEach(name -> System.out.println(name));//1.8 forEach循环
  }

You can print out all bean names

Then when spring boot starts, we need to get the corresponding startup items from the database, and we can use spring to provide listeners: After the ApplicationListener  container is initialized, only one method is provided  . Started task class, the advantage of this is that there is no need to wait for a new thread to be started to execute:

@Component
public class MyApplicationListener  implements ApplicationListener<ContextRefreshedEvent> {

  Logger logger= LoggerFactory.getLogger(MyApplicationListener.class);



  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    logger.info("-------------bean初始化完毕-------------");
    /**
     * 通过线程开启数据库中已经开启的定时任务 灵感来自spring
     * spring boot继续执行 mythread开辟线程,延迟后执行
     */
    DataSourceJobThread myThread= (DataSourceJobThread) event.getApplicationContext().getBean(
        "dataSourceJobThread");
    myThread.start();
  }

}
/**
 * @author zhuxiaomeng
 * @date 2018/1/6.
 * @email [email protected]
 *
 * 启动数据库中已经设定为 启动状态(status:true)的任务 项目启动时init
 */
@Configuration
public class DataSourceJobThread extends Thread {

  private static final Logger log = LoggerFactory.getLogger(DataSourceJobThread.class);
  @Autowired
  RoleService roleService;

  @Autowired
  JobService jobService;

  @Override
  public void run() {
    try {
      Thread.sleep(1000);
      log.info("---------线程启动---------");
      JobTask jobTask = SpringUtil.getBean("jobTask");
      SysJob job = new SysJob();
      job.setStatus(true);
      List<SysJob> jobList = jobService.selectListByPage(job);
      //开启任务
      jobList.forEach(jobs -> {
        log.info("---任务["+jobs.getId()+"]系统 init--开始启动---------");
        jobTask.startJob(jobs);
          }
      );
      if(jobList.size()==0){
        log.info("---数据库暂无启动的任务---------");
      }else
      System.out.println("---任务启动完毕---------");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

Completed, please leave a message to explain the inadequacies. This tutorial provides some services that are not given here;

However, I have implemented configurable timing tasks in the open source framework lenos. If you are interested or need help, you can download and learn:

Address: https://gitee.com/bweird/lenosp 

lenos is a rapid development scaffolding, not only has scheduled tasks, but also other technologies such as permission management, log monitoring, etc. If you like it, don't forget to click star, thank you. If you have any questions, you can add a group to ask under lenos.

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324191508&siteId=291194637