Quartz 随笔

springboot 和Quartz整合

Quartz与springboot的整合与和spring 整合相比,简单了很多。

添加依赖

	<dependency><!-- spring boot quartz starter依赖  -->
		    <groupId>org.springframework.boot</groupId>  
			<artifactId>spring-boot-starter-quartz</artifactId>  
			<version>2.0.4.RELEASE</version>
		</dependency>

Quartz

Quartz中比较重要的概念:

  • Schedule
  • Job
  • JobDatail
  • Trigger
  • JobKey
  • TriggerKey
1.Schedule

schedule是和程序交互的主要API,用于管理任务,是一个用于专门管理任务的容器。一般使用SchedulerFactory来实例化Schedule实例。

scheduler实例化后,可以进行启动(start)、暂停(stand-by)、停止(shutdown)操作,用于控制整个任务容器的生命周期。

注意:

  • scheduler被停止后,除非重新实例化,否则不能重新启动;Scheduler的生命周期时从SchedulerFactory创建他开始到scheduler调用shutdown()方法时结束
  • 只有当scheduler启动后(即使处于暂停状态也不行),即调用start()方法trigger才会被触发(job才会被执行)。
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();

JobDetail jobDetail = JobBuilder.newJob(jobClass).
               withIdentity(jobName,jobGroup).
               build();

Trigger trigger = TriggerBuilder.
				newTrigger().
				withIdentity(triggerName,triggerGroup).
				withSchedule(CronScheduleBuilder.
				cronSchedule(cronException)).
				build();
try{
	sched.schedulerJob(jobDetail,trigger);
	sched.start();
}catch(Exception e){
	e.printStackTrace();
}

2.Job

Job是一个接口,我们的业务方法一般写在它的实现类中。 当触发器被触发时就会调用Job的execute()方法。所以编写Job实现类是我们主要的工作。

import org.springframework.scheduling.quartz.QuartzJobBean

public class MyJob extends QuartzJobBean {//QuartzJobBean 继承了Job接口

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
    //具体业务逻辑
        System.out.println("xxxxxxxx\t"+ new Date());
    }
}

JobExecutionContext对象中保存了很多信息,可以通过该实例获取Scheduler,JobDetail,Trigger,

JobExecutionContext

public interface JobExecutionContext {

    //返回触发该任务的Scheduler实例
    public Scheduler getScheduler();

    //获取触发该任务的Trigger实例
    public Trigger getTrigger();

 	 //返回和该Job关联的JobDetail实例
    public JobDetail getJobDetail();

   //返回正在被执行的Job实例
    public Job getJobInstance();
}
3. Key

Quartz使用key可以唯一标识一个任务或者触发器。

  • JobKey 。用于唯一标识一个任务,通常就是通过任务名(jobName),任务组名(jobGroupName)
  • TriggerKey。唯一标识一个触发器。(triggerName, triggerGroupName)

注意:

  • 同一个分组下的Job或Trigger的名称必须唯一,即一个Job或Trigger的key由名称(name)和分组(group)组成。

下面是JobKey和Key的部分代码:

package org.quartz;

import org.quartz.utils.Key;
public final class JobKey extends Key<JobKey> {

    public JobKey(String name) {
        super(name, null);
    }
    public static JobKey jobKey(String name, String group) {
        return new JobKey(name, group);
    }
}

----------------------------------------------------------------

package org.quartz.utils;

import java.io.Serializable;
import java.util.UUID;

public class Key<T>  implements Serializable, Comparable<Key<T>> {
  
    private static final long serialVersionUID = -7141167957642391350L;
    public static final String DEFAULT_GROUP = "DEFAULT";
    private final String name;
    private final String group;
    
    /**
     * Construct a new key with the given name and group.
     */
    public Key(String name, String group) {
        if(name == null)
            throw new IllegalArgumentException("Name cannot be null.");
        this.name = name;
        if(group != null)
            this.group = group;
        else
            this.group = DEFAULT_GROUP;
    }

从上面可以看出。如果没有设置组名,则使用默认的组名“DEFAULT”

TriggerKey和上面一样,唯一不同的是用于唯一标识触发器

4.JobDetail

上面介绍了JobKey,创建JobDetail实例的时候就需要制定JobKey。
JobDetail中包含任务的各种属性设置(jobNmae,JobGroup,Job实现类等),以及用于包装Job接口的实例,并且可以保存一些额外的业务信息,这些信息被保存在JobDataMap(类Map结构,只是提供了一些更方便获取数据的方法)

JobDetail实例的创建

 JobDetail jobDetail = JobBuilder.newJob(jobClass).//jobClass 被执行的任务
                withIdentity(jobName,jobGroup).//jobName-jobGroup 必须唯一
                usingJobData(jobDataClass). //封装一些业务信息保存在JobDataMap中 
                build();

使用withIdentity来指定JobKey。

在JobBuilder中重载了withIdentity()方法

  /**
     * Use a <code>JobKey</code> with the given name and default group to
     * identify the JobDetail.
     * <p>If none of the 'withIdentity' methods are set on the JobBuilder,
     * then a random, unique JobKey will be generated.</p>
     */
    public JobBuilder withIdentity(String name) {
        key = new JobKey(name, null);
        return this;
    }  
    

    public JobBuilder withIdentity(String name, String group) {
        key = new JobKey(name, group);
        return this;
    }
   
    public JobBuilder withIdentity(JobKey jobKey) {
        this.key = jobKey;
        return this;
    }
Trigger

Trigger是Quartz另一个特别重要的概念,用于控制任务的执行时间,频次等。
Trigger用于触发Job的执行。当你准备调度一个job时,你创建一个Trigger的实例,然后设置调度相关的属性。Trigger也有一个相关联的JobDataMap,用于给Job传递一些触发相关的参数。Quartz自带了各种不同类型的Trigger,最常用的主要是SimpleTrigger和CronTrigger。

  • SimpleTrigger,主要用于一次性执行的Job(只在某个特定的时间点执行一次),或者Job在特定的时间点重复执行N次,每次执行间隔T个时间单位
  • CronTrigger。基于日历的调度上非常有用,如“每个星期五的正午”,或者“每月的第十天的上午10:15”等

一个任务和关联多个触发器,这样一个任务就可以在不同时间执行。所以这样对任务的一些操作会涉及到触发器的执行。

  • 当暂停一个Job任务时,会暂停该任务关联的所有触发器。
  • 如果只是暂停一个触发器,只会暂停指定的触发器。
  • 如果移除一个任务,则相关联的触发器也会被移除
  • 如果移除触发器,只会移除该触发器,不会影响任务关联的其他触发器。
1. SimpleTrigger触发器 ,人如其名(简单)。该实例一般由SimpleScheduleBuilder创建,提供了很多方便的方法,用于指定触发器的频次。
	//每分钟执行一次
	 public static SimpleScheduleBuilder repeatMinutelyForever() {
    }
     //每隔xxx分钟执行一次
    public static SimpleScheduleBuilder repeatMinutelyForever(int minutes) {

    }
    //每秒执行一次
    public static SimpleScheduleBuilder repeatSecondlyForever() {
    }
    //每隔xxx秒执行一次
    public static SimpleScheduleBuilder repeatSecondlyForever(int seconds) {
    }
    
    //设置,每分钟执行的任务的总执行次数
    public static SimpleScheduleBuilder repeatMinutelyForTotalCount(int count) {
    }
    //
    public static SimpleScheduleBuilder repeatMinutelyForTotalCount(int count, int minutes) {
    }
2. CronTrigger

这个触发器实际开发使用的比较多,因为它提供了一套按日历触发的简单操作。适合各种复杂的操作
其中最为关键的就是CronExpression了

Cron-Expressions用于配置CronTrigger的实例。
Cron Expressions是由七个子表达式组成的字符串,用于描述日程表的各个细节。这些子表达式用空格分隔,并表示:

  • Seconds //范围[0-59]
  • Minutes //范围[0-59]
  • Hours //范围[0-23]
  • Day-of-Month //范围[1-31]
  • Month //范围[0-11] ,或 JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC
  • Day-of-Week //范围 可以是字符串 “SUN,MON,TUE,WED,THU,FRI和SAT”,也可以是数字[1-7] (1代表周日)
  • Year (optional field)

表达式的值可以使用通配符。使用规则如下

  • ‘*’:表示该字段可以为任意合法的值。 如果用于秒字段,则表示每秒
  • ‘/’: 用于指定值的增量。例如,如果用在秒字段“0/15”,表示从零秒开始每隔15秒执行一次。和”15,30,45,0“表示的一样。
  • ‘?’ :只允许在Day-of-Month 和Day-of-Week字段用于两选一。
  • “L”:允许用于月日和星期字段。这个角色对于“最后”来说是短暂的,但是在这两个领域的每一个领域都有不同的含义。例如,“月”字段中的“L”表示“月的最后一天” - 1月31日,非闰年2月28日。如果在本周的某一天使用,它只是意味着“7”或“SAT”。但是如果在星期几的领域中再次使用这个值,就意味着“最后一个月的xxx日”,例如“6L”或“FRIL”都意味着“月的最后一个星期五”。您还可以指定从该月最后一天的偏移量,例如“L-3”,这意味着日历月份的第三个到最后一天。当使用’L’选项时,重要的是不要指定列表或值的范围,因为您会得到混乱/意外的结果。
  • “W”用于指定最近给定日期的工作日(星期一至星期五)。例如,如果要将“15W”指定为月日期字段的值,则意思是:“最近的平日到当月15日”。
  • '#'用于指定本月的“第n个”XXX工作日。例如,“星期几”字段中的“6#3”或“FRI#3”的值表示“本月的第三个星期五”。

0/5 * * * * ? 每隔5秒执行一次
0 0 18 * * ? 每天下午6点执行
12 12 * * * ? 每小时第12分钟12秒执行

参考文献;
Quartz官方文档

动态创建任务的demo

下面的建议版本没有涉及数据,一般来说为了防止服务器重启导致的数据丢失。应当将这些数据保存在数据库中。

@Controller
@RequestMapping("quartz")
public class QuartzController {
    private static final String DEFAULT_JOB_GROUP="job_group";
    private static final String DEFAULT_TRIGGER_GROP="trigger_group";
    private static final AtomicInteger  INCREMENT= new AtomicInteger(0);
    
    @Autowired
    QuartzUtil quartzUtil;
    
    /**
    *根据自己的需要增加接受参数。
    */
    @RequestMapping("/addSimpleQuartz")
    @ResponseBody
    public String addSimpleQuartz(String time){
        try {
            QuestionnaireJobDTO job =new QuestionnaireJobDTO();
            job.setTriggerName("trigger"+INCREMENT.incrementAndGet());//触发器名称
            job.setJobName("job"+INCREMENT.get());//任务名称
            job.setJobGroup(DEFAULT_JOB_GROUP);//任务组
            job.setTriggerGroup(DEFAULT_TRIGGER_GROP);//触发器组
            SimpleDateFormat format =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date = format.parse(time);//任务执行时间
            
            JobDataMap map = new JobDataMap();
            map.put("zz", "cccc");
            //此处的任务实现类,可以改为输入参数。更好一点的是,传入一个类的全限定类名。这样可以不重启服务器就可以方便的添加任务了。
            //Class jobClass = Class.forName(className);
            quartzUtil.addSimpleJob(QuestionnaireJob.class,date, "job", "trigger",map);//添加一个简单任务,在指定时间执行
        }catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
        return "success";
    } 
}

创建一个任务

public class QuestionnaireJob  extends QuartzJobBean{
	
	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		System.out.println(" executing .............");
	}

	
	
}

quartz工具类,用于实际操作任务

package com.test.quartz;

import static org.quartz.TriggerBuilder.newTrigger;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;



@Component
public class QuartzUtil {
    public static final String DEFAULT_JOB_GROUP="JOB_GROUP";
    public  static final String DEFAULT_TRIGGER_GROUP="TRIGGER_GROUP";
  
    @Autowired
    Scheduler scheduler;
    
    
    /**
     * 
     *基于日历的调度, 例如每个星期五,需要使用cronExpression表达式,来指定时间
     *@author zhangcc
     *@date 2018年8月17日 
     *@param jobClass
     *@param cronException  cron表达式  
     *@param jobName 任务名称,在一个任务组内需要唯一
     *@param jobGroup 任务组
     *@param triggerName 触发器名称,需要在触发器组内唯一
     *@param triggerGroup 触发器组
     *@return 
     * @throws SchedulerException 
     */
    public void addJob(Class<? extends Job> jobClass,String cronException, String jobName, String jobGroup, String triggerName, String triggerGroup) throws SchedulerException{

        JobDetail jobDetail = JobBuilder.newJob(jobClass).
                withIdentity(jobName,jobGroup).
                build();

        Trigger trigger = newTrigger().
                withIdentity(triggerName,triggerGroup).
                withSchedule(CronScheduleBuilder.cronSchedule(cronException)).
                build();
        scheduler.scheduleJob(jobDetail,trigger);
        startSchedule();
    }

    public void addJob(Class<? extends Job> jobClass,String cronException, String jobName, String triggerName) throws SchedulerException{
        addJob(jobClass,cronException,jobName,DEFAULT_JOB_GROUP,triggerName,DEFAULT_TRIGGER_GROUP );
    }
    
    /**
     * 添加一个简单的任务, 某个具体时间执行
     *@author zhangcc
     *@date 2018年8月17日 
     *@param jobClass
     *@param date     任务执行时间
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void addSimpleJob(Class<? extends Job> jobClass, Date date, String jobName, String triggerName) throws SchedulerException{
    	addSimpleJob(jobClass, date, jobName, triggerName, null);
    }
    
    /**
     * 添加一个简单的任务, 某个具体时间执行
     *@author zhangcc
     *@date 2018年8月22日 
     *@param jobClass
     *@param date     任务执行时间
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@param argsMap  需要传递的一些参数
     *@return void
     * @throws SchedulerException 
     */
    public void addSimpleJob(Class<? extends Job> jobClass, Date date, String jobName, String triggerName,JobDataMap argsMap) throws SchedulerException{
        addSimpleJob(jobClass, date, jobName, null, triggerName, null, argsMap);
    }
    
    public void addSimpleJob(Class<? extends Job> jobClass, Date date, String jobName, String jobGroup,String triggerName,String triggerGroup,JobDataMap argsMap) throws SchedulerException{
    	 JobBuilder jobBuilder =JobBuilder.newJob(jobClass). withIdentity(jobName,DEFAULT_JOB_GROUP);
         
         if(argsMap != null)
         	jobBuilder.usingJobData(argsMap);
         
         JobDetail jobDetail = jobBuilder.build();
         Trigger trigger = newTrigger().
                 withIdentity(triggerName,DEFAULT_TRIGGER_GROUP).startAt(date).
                 build();
         scheduler.scheduleJob(jobDetail,trigger);
         startSchedule();
    }
   
     /**
     * 继续执行
     *@author 
     *@date 2018年8月22日 
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void resumeJob(String jobName,String jobGroup) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
    	if(scheduler.checkExists(jobKey))
    		scheduler.resumeJob(jobKey);
    }
    
     /**
     * 继续执行触发器
     *@author 
     *@date 2018年8月22日 
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void resumeTrigger(String triggerName,String triggerGroup) throws SchedulerException{
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName,triggerGroup);
    	if(scheduler.checkExists(triggerKey))
    		scheduler.resumeTrigger(triggerKey);
    }
     /**
     * 暂停任务。该操作会暂停该任务锁绑定的所有触发器。如果触发器A,B都绑定了任务JOBA,如果暂停JOBA则会暂停触发器A和B
     *@author 
     *@date 2018年8月22日 
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void pauseJob(String jobName,String jobGroup) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
        if(scheduler.checkExists(jobKey))
        	scheduler.pauseJob(jobKey);
    }
     /**
     * 暂停触发器。只会暂停该触发器
     *@author 
     *@date 2018年8月22日 
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void pauseTrigger(String triggerName,String triggerGroup) throws SchedulerException{
    	TriggerKey triggerKey = TriggerKey.triggerKey(triggerName,triggerGroup);
    	if(scheduler.checkExists(triggerKey))
    		scheduler.pauseTrigger(triggerKey);
    }
	 /**
     * 移除任务
     *@author 
     *@date 2018年8月22日 
     *@param jobName 任务名称,需要唯一
     *@param triggerName  触发器名称,需要唯一
     *@return void
     * @throws SchedulerException 
     */
    public void removeJob(String jobName,String jobGroup) throws SchedulerException{
        JobKey jobKey = JobKey.jobKey(jobName,jobGroup);
    	if(scheduler.checkExists(jobKey))
    		scheduler.deleteJob(jobKey);
    }
    
    public void startSchedule() throws SchedulerException{
        if(scheduler.isShutdown()){
            scheduler.resumeAll();
        }else
            scheduler.start();
    }
	// 获取当前程序所有的任务
	 public void listJobs() throws SchedulerException{
    	List<String> jobGroups = scheduler.getJobGroupNames();
    	for(String jobGroup : jobGroups) {
    		GroupMatcher<JobKey> groupMatcher=GroupMatcher.jobGroupEquals(jobGroup);
    		Set<JobKey> jobs =scheduler.getJobKeys(groupMatcher);
    		System.out.println("jobGroup\t"+jobGroup);
    		for(JobKey jobKey : jobs) {
    			System.out.println("\t"+jobKey.getName());
    		}
    	}
    	
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值