JAVA基础--Quartz定时任务调度的简单应用(1)

      上次学习了Timer,我们知道了在项目中使用Timer有很多不足之处,那么对那些复杂的定时调度任务,我们应该怎么处理呢? 这个时候Quartz就闪亮登场啦,它会帮我们解决这些难题;

    1,  首先我们来说一下 Quartz 与 Timer 的区别:

        (1), 出身不同, Timer 为JDK自带; Quartz 为开源项目;
        (2), 能力区别, Timer 远没有 Quartz 功能强大和完善, Timer 不能搞定的交给 Quartz ;
        (3), 底层机制不同, Timer 利用后台子线程实现, Quartz 可以用于线程池中;

    2, Quartz简介:

        OpenSymphony提供的强大的开源任务调度框架;

        官网: http://www.quartz-scheduler.org/

        Maven仓库:

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

3, 好了,先来个HelloWorld:

    (1),引入Jar包;(不用Maven的话需要引入: quartz-2.3.0.jar, slf4j-api-1.7.25.jar)

    (2),创建任务:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job{

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // TODO 具体的业务逻辑:
        System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
    }

}

    (3),调度任务:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        // 创建一个 JobDetail 实例,将该实例与 HelloJob Class 绑定;
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob").build();
        // 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1).repeatForever())
                .build();
        // 创建 Scheduler 实例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("当前时间:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

4, 现在我们再来分析一下上面的例子:

    (1), 首先是我们要创建一个Job: HelloJob; Job的应用及生命周期:

/**
 * (1),Job接口非常容易实现,只有一个execute方法,类似TimerTask的run方法,在里面编写业务逻辑;
 * (2),Job实例在Quartz中的生命周期: 
 *      每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例.当调用完成后,关联的Job对象实例会被释放,之后会被垃圾回收;
 **/
public class HelloJob implements Job{

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        // TODO 具体的业务逻辑:
        
    }

}

 (2), 之后需要调用我们的HelloJob,那么就有了JobDetail:

 public class Test{
     public static void main(String[] args) throws SchedulerException {
       /**
         * 创建一个 JobDetail 实例,将该实例与 HelloJob Class 绑定;
         * 
         * JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,
         * 它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
         * 重要属性: name, group, jobClass, jobDataMap
         * */
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob","group1").build();
        //我们可以通过 jobDetail.getKey() 来获取这些属性,方便以后调度;
        System.out.println("jobDetail name is "+jobDetail.getKey().getName());
        System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
        System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
    }
}

5, 通过 JobExecutionContext 来获取向 Job 中传入的参数:

(1), 传入参数:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        /**
         * 创建一个 JobDetail 实例,将该实例与 HelloJob绑定;
         * 
         * JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
         * 重要属性: name, group, jobClass, jobDataMap
         * */    
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                //传入JobDetail的属性及参数
                .withIdentity("myJob","group1")
                .usingJobData("message", "HelloJobDetail")
                .usingJobData("FloatMessage", 2.2F)
                .build();
        
        //通过 getKey()获取相关属性;
        System.out.println("jobDetail name is "+jobDetail.getKey().getName());
        System.out.println("jobDetail group is "+jobDetail.getKey().getGroup());
        System.out.println("jobDetail jobClass is "+jobDetail.getJobClass());
        
        // 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
        Trigger trigger = TriggerBuilder.newTrigger()

                //传入Trigger(触发器)的属性及参数
                .withIdentity("myTrigger")
                .usingJobData("message", "HelloTrigger")
                .usingJobData("DoubleMessage", 2.4D)

                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(1).repeatForever())
                .build();
        
        // 创建 Scheduler 实例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("当前时间:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

(2), 获取参数:

package quartz;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;

public class HelloJob implements Job{
    /**
     * JobExecutionContext :
     * (1), 当Scheduler(调度器) 调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
     * (2), Job能通过JobExecutionContext对象访问到Quartz运行时候的环境及Job本身的明细数据;
     **/
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException { 
        //获取传入的任务属性信息;
        JobKey jobKey = context.getJobDetail().getKey();
        System.out.println("Job(任务) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
        //获取传入的触发器属性信息;
        TriggerKey triggerKey = context.getTrigger().getKey();
        System.out.println("Trigger(触发器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());


        /**
         * JobDataMap: 
         *  (1), 在进行任务调度时JobDataMap存储,在JobExecutionContext中,非常方便获取.
         *  (2), JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时,这些参数对象会传递给它.
         *  (3), JobDateMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型.
         *  获取方试:
         * */
        //获取传入的Job参数信息;
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
        //获取传入的Trigger参数信息;
        JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
        System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));

        
        /* --------------获取JobDataMap方法二: 合并获取--------------- */
        JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
        System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage")+"\n");
        /**
         * 测试证明: 当Trigger与JobDetail中传入的Key值相同,使用mergedJobDataMap获取,则优先获取Trigger中的Key值;JobDetail中的Key值被覆盖掉;
         * */
        
    }
}

(3), 通过set,get方法获取参数:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.TriggerKey;

/**
 * (1),Job接口非常容易实现,只有一个execute方法,类似TimerTask的run方法,在里面编写业务逻辑;
 * (2),Job实例在Quartz中的生命周期: 
 *      每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例.当调用完成后,关联的Job对象实例会被释放,之后会被垃圾回收;
 **/
public class HelloJob implements Job{
    
    private String message;
    private float FloatMessage;
    private double DoubleMessage;
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public float getFloatMessage() {
        return FloatMessage;
    }
    public void setFloatMessage(float floatMessage) {
        FloatMessage = floatMessage;
    }
    public double getDoubleMessage() {
        return DoubleMessage;
    }
    public void setDoubleMessage(double doubleMessage) {
        DoubleMessage = doubleMessage;
    }


    /**
     * JobExecutionContext是什么:
     * (1), 当Scheduler(调度器) 调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;
     * (2), Job能通过JobExecutionContext对象访问到Quartz运行时候的环境及Job本身的明细数据;
     **/
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // TODO 具体的业务逻辑:
        System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
        
        //获取传入的任务属性信息;
        JobKey jobKey = context.getJobDetail().getKey();
        System.out.println("Job(任务) : name: "+jobKey.getName()+", group: "+jobKey.getGroup());
        //获取传入的触发器属性信息;
        TriggerKey triggerKey = context.getTrigger().getKey();
        System.out.println("Trigger(触发器) : name: "+triggerKey.getName()+", group: "+triggerKey.getGroup());
        
        /**
         * JobDataMap: 
         *  (1), 在进行任务调度时JobDataMap存储,在JobExecutionContext中,非常方便获取.
         *  (2), JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时,这些参数对象会传递给它.
         *  (3), JobDateMap实现了JDK的Map接口,并且添加了一些非常方便的方法用来存取基本数据类型.
         *  获取方试:
         * */
        //获取传入的Job参数信息;
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        System.out.println("Job param: "+jobDataMap.getString("message")+", "+jobDataMap.getFloat("FloatMessage"));
        //获取传入的Trigger参数信息;
        JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
        System.out.println("Trigger param: "+triggerDataMap.getString("message")+", "+triggerDataMap.getDouble("DoubleMessage"));
        
        /* --------------获取JobDataMap方法二: 合并获取--------------- */
        JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
        System.out.println("ALL param: "+mergedJobDataMap.getString("message")+", "+mergedJobDataMap.getFloat("FloatMessage")+", "+mergedJobDataMap.getDouble("DoubleMessage"));
        /**
         * 测试证明: 当Trigger与JobDetail中传入的Key值相同,使用mergedJobDataMap获取,则优先获取Trigger中的Key值;JobDetail中的Key值被覆盖掉;
         * */
        
        /* --------------获取JobDataMap方法三: 属性 get,set--------------- */
        System.out.println("ALL get param: "+message+", "+FloatMessage+", "+DoubleMessage+"\n");
        /**
         * Job实现类中创建setter方法对应JobDataMap的键值(Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法);
         * */
        
        
    }
}

6, Trigger 设置开始/结束 触发时间:

package quartz;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        /**
         * 创建一个 JobDetail 实例,将该实例与 HelloJob绑定;
         * 
         * JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来储存特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例.
         * 重要属性: name, group, jobClass, jobDataMap
         * */
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class).withIdentity("myJob","group1")
                .usingJobData("message", "HelloJobDetail")
                .usingJobData("FloatMessage", 2.2F)
                .build();
        
        // 创建一个Trigger实例,定义该job立即执行,并且每隔1秒钟重复执行一次;
        
        /**
         * Trigger是Quartz中的出触发器,用来告诉调度任务作业什么时候触发.即Trigger对象是用来触发Job的;
         * 通用属性:
         *  JobKey: 表示Job实例的标识,触发器被触发时,该指定的Job实例会被执行;
         *  StartTime: 表示触发器的时间表 首次被触发的时间.值为java.util.Date;
         *  EndTime: 指定触发器的不在被触发的时间.值的类型是java.util.Date;
         * */
        //开始执行时间为当前时间的3秒后
        Date startTime = new Date();
        startTime.setTime(startTime.getTime()+3000);
        //结束执行时间为当前时间的6秒后
        Date endTime = new Date();
        endTime.setTime(endTime.getTime()+6000);
        
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .usingJobData("DoubleMessage", 2.4D)
                
                //设置开始/结束时间
                .startAt(startTime)
                .endAt(endTime)
                
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
                .build();
        
        // 创建 Scheduler 实例;
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        System.out.println("当前时间:"+sf.format(calender.getTime()));
        
        scheduler.start();
        
    }
}

Job中也可以获取 开始/结束 时间:

public class HelloJob implements Job{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        Calendar calender = Calendar.getInstance();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // TODO 具体的业务逻辑:
        System.out.println("Hellow Quartz! 执行时间:"+sf.format(calender.getTime()));
        
        /* --------------------------获取开始/结束时间--------------------- */
        Trigger trigger = context.getTrigger();
        Date startTime = trigger.getStartTime();
        Date endTime = trigger.getEndTime();
        System.out.println("StartTime is "+sf.format(startTime));
        System.out.println("EndTime is "+sf.format(endTime)+"\n");        
    }
}

7, SimpleTrigger设置执行条件:


        /**
         * SimpleTrigger: 在一个指定时间段内执行一次任务,或在指定时间间隔内多次执行任务;
         * 注意: (1)重复次数可以为0,正整数或是 SimpleTrigger.REPEAT_INDEFINITELY 常量值.
         *      (2)重复执行时间间隔必须为0或长整数;
         *      (3)一担被指定了endTime参数,那么它会覆盖重复次数参数的效果;
         * */
        SimpleTrigger trigger = (SimpleTrigger)TriggerBuilder.newTrigger()
                .withIdentity("myTrigger")
                .usingJobData("DoubleMessage", 2.4D)
                
                //设置开始/结束时间
                .startAt(startTime)
                .endAt(endTime)
                
                //设置多次执行条件
                .withSchedule(
                        SimpleScheduleBuilder
                        .simpleSchedule()
                        //时间间隔,单位有: 秒, 毫秒, 分钟, 小时
                        .withIntervalInSeconds(1)
                        //循环执行
//                        .repeatForever()
                        //执行次数, (参数+1)次, 参数为0执行1次,参数为1执行2次,...; 参数为-1,则执行无穷
                        .withRepeatCount(-1)
                        )
                
                .build();

到这我们已经对Quartz有了一定的认识,那么下次再来分享更复杂的任务调度方式;看看怎么在某个节日来调度的.

猜你喜欢

转载自my.oschina.net/u/3681868/blog/1608064