学习Quartz(1)

什么是Quartz


Quartz是一个完全由Java编写的开源调度框架,作用是在Java应用程序中进行作业调度提供简单而强大的机制。Quartz允许开发人员根据时间间隔来调度任务。它实现了作业和出发起的多对多的关系,还能把作业与不同的触发器关联。简单地创建一个org.quartz.Job接口的Java类,Job接口只包含一个方法:

public void execute(JobExecutionContext context) throws JobExecutionException;

在Job接口的实现类里面。添加需要的业务逻辑到execute()方法中去。配置好Job实现类并设定好调度时间表,Quartz就会在设定的时间点调度作业并执行execute()方法。

整合了Quartz的应用程序可以重用不同事件的作业,还可以作为一个时间组合多个作业。Quartz通过属性文件来配置JDBC事务的数据源、全局作业、触发器侦听器、插件、线程池等。

Quartz室友James House创建并最初于2001年春天被加入到sourceforge工程。接下来的几年里,有很多的新特性和版本出现,但是直到项目迁移到显得站点并成为OpenSymphony项目家族的一员,才开始真正启动并收到广泛的关注。

目前的版本已经是2.0以上了,v2.x相对于v1.x有很对新特性出现,并有很多的改动,具体参见Quartz官网上说明。这里介绍的仍然是v1.x(v1.8.6)。

“hello,Quartz"

配置环境:

1.下载Quartz

2.阅读Readme.txt,了解每个Jar包的作用,将Quartz.jar包金额lib/下的几个Jar包,以及相关依赖的Jar包放在工程的classpath中去。

先来看一个简单的Quartz莹莹,让它每个五秒输出”hello,Quartz",一共输出10次。

代码1:创建任务

//引入包
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionException;
import org.quartz.JobExecutionContetx;

//创建自己的一个继承类
public class HelloQuartzJob extends Job{

  //实现其唯一的execute()方法
  public void execute(JobExecutionContext context) throws JobExecutionException{
   System.out.println("hello,Quartz!---executing its JOB at"+new Date()+"by"+context.getTrigger().getName();
  } 

}

为了能够执行这个任务,首先我们得得到一个Schedule实例,然后创建一个包含任务信息的JobDetail,最后创建一个Trigger管理任务的执行。

代码2:调度任务

//引入包
import java.sql.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;

//创建类
public class HelloQuartzScheduling{

  public static void mian(String[] args){
  
   //创建调度工厂  
   SchedulerFactory schedulerFactory=new StdSchedulerFactory();
   //创建调度
   Scheduler scheduler=schedulerFactory.getScheduler();
   //创建任务细节
   JobDetail jobDetai=new JobDetail("helloQuartzJob",Scheduler.DEFAULT_GROUP,HelloQuartzJob.class);
   //创建简单触发器
   SimpleTrigger simpleTrigger=new SimpleTrigger("simpleTrigger",Scheduler.DEFAULT_GROUP);
   //设置触发器启动时间
   simpleTrigger.setStartTime(new Date(System.currentTimeMillis());
   //设置触发器执行间隔(毫秒计)
   simpleTrigger.setRepeatInterval(5000);
   //设置触发器触发次数
   simpleTrigger.setRepeatCount(10);
   //设置调度的任务
   scheduler.schedulerJob(jobDetail,simpleTrigger);
   //启动时刻表
   scheduler.start();
  }
}

下面是运行的输出情况:

从上图我们可以发现如果设置触发器的触发次数为10次的话,会输出11次,因此实际生产中,我们得把触发次数主动减一次。

Job接口包含了唯一方法execute(),我们需要将任务逻辑添加到里面去。StdSchedulerFactory.getScheduler()的作用是返回一个SchedulerFactory的实例,然后我们在创建JobDetail实例,并且传递三个参数(“任务名”,任务组名,实现任务的类)。第一个参数任务名是用于引用该任务;第二个参数任务组名,这里使用默认名,任务组名主要用来引用集合起来的一组任务,比如说可以使用Scheduler.pauseJobGroup()来暂停一组任务,每个组中的任务名是唯一的;第三个参数时实现任务的类。创建完JobDetail后,我们还得创建一个Trigger,这里使用的SimpleTrigger类,它提供了Java中Timer风格的触发器行为。传递给SimpleTrigger构造方法的两个参数分别是触发器名和任务组名,触发器名在它所在的组必须是唯一的。接下来我们的设置一下触发器的一些属性,setStartTime()是用来设置启动时间的,setRepeatInterval()是用来设置触发间隔的,要注意单位是毫秒,setRepeatCount()是设置触发次数。最后,通过scheduler.start()来启动调度,也可以通过stop()来终止调度。

CronTrigger类

Quartz有两大触发器,除了上面的Simpletrigger外,还有CronTrigger。CronTrigger能够提供对复杂的触发器表达式的支持。CronTrigger时基于Unix Cron守护进程的,它是一个调度程序,支持简单而强大的触发器语法。

使用Cron Trigger主要的是掌握Cron表达式。Cron表达式包含6个必要组件和1个可选组件,如下表所示。

位置含义允许的特殊字符
1秒(0-59)  ,  -  *  /
2分(0-59)  ,  -  *  /
3小时(0-24)  ,  -  *  /
4日期(1-31)  ,  -  *  /  ?  L  W  C
5月(JAN-DEC或1-12)  ,  -  *  /
6星期(SUN-SAT或1-7)  ,  -  *  /  ?  L  C  #
7年(可选,1970-2099),若为空,表示全部时间范围  ,  -  *  /

特殊字符的含义,见下表。

特殊字符说明
*通配符,任意值
?无特定值。通常和其他指定的值一起使用,表示必须显示该值但不能检查
-范围。e.g.小时域中10-12表示10:00,11:00,12:00
,列分隔符。可以让你指定一系列的值。e.g.在星期域中指定MON,TUE和WED
/增量。表示一个值的增量。e.g.分钟域中0/1表示从0开始,每次增加1min
L表示Last。它在日期域和星期域中表示有所不同。在日期域中,表示这个月的最后一天,而在星期域中永远是7(即星期六)。当你希望使用星期域中的某一天时,L字符非常有用。e.g.星期域中6L表示每个月的最后一个星期五
W在本月内里当天最近的工作日出发,所谓的最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离为0;所谓本月内是指不能跨月渠道最近工作日,即使前/后月份的最后一天或者第一天满足最近工作日的条件。e.g.LW表示这个月的追后一个工作日触发,W强烈依赖于月份
#表示该月的第几个星期,e.g.1#2表示每个月第一个星期的星期一
C日历值。日历值时更具一个给定的日历计算出来的。在日历值域中给定一个20C将在20日(日历包括20日)或20日后日历中包含的第一天(不包括20日)激活触发器。e.g.在星期域中使用6C表示日历中星期五(日历包括星期五)或者第一天(日历不包括星期五)

Cron表达式举例:

“30 * * * * ?”每半分钟触发任务

“30 10 * * * ?"每小时的10分30秒触发任务

”30 10 1 * * ?“每天的1点10分30秒触发任务

”30 10 1 20 * ?“每月20号的1点10分30秒触发任务

”30 10 1 20 10 ? *“每年的10月20号的1点10分30秒触发任务

”30 10 1 30 10 ? 2011“2011年的10月20号1点10分30秒触发任务

”30 10 1 ? 10 * 2011“2011年的10月每天1点10分30秒触发任务

”30 10  1 ? 10 SUN 2011“2011年的10月的每个星期天的1点10分30秒触发任务

”15,30,45 * * * * ?“每15秒,30秒,45秒触发任务

”15-45 * * * *?“在15到45秒内,每秒都触发任务

”15/5 * * * *?“每分钟的15秒开始触发,每隔5秒触发一次

”15-30/5 * * * * ?“每分钟的15秒到30秒之间触发任务,每隔五秒触发一次

”0 0/3 * * * ?"每小时的0分0秒开始,每隔3分钟触发任务

“0 15 10 ? * MON-FRI"星期一到星期五的10点15分触发任务

”0 15 10 L * ?“每个月的最后一天的10点15分触发任务

”0 15 10 LW * ?"每个月的最后一个工作日的10点5分触发任务

“0 15 10 ? * 5L"每个月的最后一个星期四的10点15分触发任务

”0 15 10 ? * 5#3“每月的第3个星期的星期四的10点15分触发任务

接着,我们可以尝试下将上面的SimpleTrigger的例子改成CronTrigger,代码如下:

//引入包
import java.text.ParseException;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;

public class HelloQuartzScheduling{

  public static void main(String[] args)throws ParseException,SchedulerException{

  //构造调度工厂
  SchedulerFactory schedulerFactory=new StdSchedulerFactory();
  //创建调度
  Scheduler scheduler=schedulerFactory.getScheduler();
  //创建任务细节
  JobDetail jobDetail=new JobDetail("jobDetail",Scheduler.DEFAULT_GROUP,HelloQuartzJob.class);
  //创建Cron触发器触发规则
  String cronExpression="30/5 * * * * ?";//每分钟的30秒开始,每个5秒触发一次任务
  //创建Cron触发器
  CronTrigger cronTrigger=new CronTrigger("cronTrigger",Scheduler.DEFAULT_GROUP,cronExpression);
  //在调度中添加任务和触发器
  scheduler.scheduleJob(jobDetail,cronTrigger);
  //启动触发器
  cronTrigger.start();
  }

}

输出结果如图所示:

CronTrigger在使用HolidayCalendar类时还可以做到排除某一段时间,比如说国庆节不执行调度任务,代码示例如下:

//引入包
import java.util.Calendar;
import java.text.ParseException;
import java.quartz.CronTrigger;
import java.quartz.JobDetail;
import java.quartz.Scheduler;
import java.quartz.SchedulerException;
import java.quartz.SchedulerFactory;
import java.quartz.impl.StdSchedulerFactory;
import java.quartz.impl.calendar.HolidayCalendar;

public class HelloQuartzScheduling{

  public static void main(String[] args)throws ParseException,SchedulerException{
   
    //创建调度工厂
    SchedulerFactory schedulerFactory=new StdSchedulerFactory();
    //创建调度
    Scheduler scheduler=schedulerFactory.getScheduler(0;
    //创建任务
    JobDetai jobDetail=new JobDetail("jobDetail",Scheduler.DEFAULT_GROUP,HelloQuartzJob.class);
    //创建日历
    Calendar cal=Calendar.getInstance();
    //设置日历属性
    cal.set(2012,Calendar.OCTOBER,1);
    //创建假期日历
    HolidayCalendar holidayCal=new HolidayCalendar();
    //排除假期的那一天
    holidayCal.addExculdedDate(cal.getTime());
    //在调度中去除假期
    //addCalendar(String calName,Calendar calendar,boolean replaceboolean updateTrigger)
    scheduler.addCalendar("calendar",holidayCal,true,false);
    //创建Cron触发器    
    CronTrigger cronTRigger=new CronTrigger("cronTrigger",Scheduler.DEFAULT_GROUP,cronExpression);
    //Cron触发器中设置假期
    cronTrigger.setCalendarName("calendar");
    //在调度中加入任务及触发器
    scheduler.scheduleJob(jobDetail,cronTrigger);
    //启动触发器
    scheduler.start();
  }

}

JobStore:任务持久化

Quartz支持任务持久化,这可以让你在运行时增加任务或者对现存的任务进行修改,并为后续任务的执行持久化这些变更和增加的部分。中心概念时JobStore接口。默认的华师RAMJobStore。

配置文件

上述没有用到任何的配置文件。Quartz支持配置文件,它的好处时比编写代码简单,且以后不需要重新编译源码。

>>配置quartz.properties特性文件

quartz.properties文件定义了Quartz应用运行时的行为,还包含了许多空值Quartz运转的属性。它应该放在工程的classpath中。

代码清单5:quartz.properties

#==========================================================================================
#Configure Main Scheduler Properties
#==========================================================================================


#实例名
org.quartz.scheduler,instanceName=QuartzScheduler
#实例ID
org.quartz.scheduler,instanceId=AUTO


#==========================================================================================
#Configure ThreadPool
#==========================================================================================
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
#线程个数
org.quartz.threadPool.threadCount=3
org.quartz.threadPool.threadPriority=5


#==========================================================================================
#Configure JobStore
#==========================================================================================
org.quartz.jobStore,misfireThreshold=60000
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore


#==========================================================================================
#Configure Plugins
#==========================================================================================
org.quartz.plugin.triggerstory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
#org.quartz.plugins.xml.JobInitializationPlugin时Quartz自带的插件
#默认时,这个插件会在classpath中搜索名为quartz_jobs.xml
#的文件,并从中加载Job和Trigger信息
#v1.8之前用JobInitializationPlugin
#org.quartz.plugin.jobInitiaization.class=org.quartz.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.class=
org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
org.quartz.plugin.jobInitializer.fileNames=quartz_jobs.xml
org.quartz.plugin.jobInitializer,failOnFieldNotFound=true
org.quartz.plugin.jobInitializer.scanInterval=10
org.quartz.plugin.jobInitializer.wrapInUserTransaction=false



#关闭quartz新版本检测功能
org.quartz.scheduler.skipUpdateCheck=true

>>配置quartz_jobs.xml文件

在配置quartz_jobs.xml时,遇到一个问题:

因为quartz从版本1.8开始,配置文件有所改动,以前quartz自带的插件是JobInitializationPlugin,而1.8中的时XMLSchedulingDataProcessorPlugin.xml,schema亦有所改变。难道是改变后的配置不对了?错误提示是插件类找不到,jar包也都加入到了工程里了。最终终于发现,在quartz.properties特性文件中配置查建行最后多打了个分号。原来是一个多余的分号引发的错误。

下面时新的xml配置文件格式实例。

代码清单6:quartz_jobs.xml格式

<?xml version="1.0" encoding="UTF-8"?>
<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.ogr/xml/job_scheduling_data_1_8.xsd" 
   version="1.8"> 
<pre-processing-commands>
  <!--在执行作业和触发器之前执行的命令-->
  <delete-jobs-in-group>*</delete-jobs-in-group>
  <!--删除标示组中的所有作业,如果是”*“,则删除所有作业组中的作业,同时也会删除与作业相关的触发器-->
  <delete-triggers-in-group>*</delete-triggers-in-group>
  <delete-job>
     <!--删除指定的作业,同时也会删除与之关联的触发器-->
     <name></name>
     <group></group>
  </delete-job>
  <delete-trigger>
     <!--指定要删除的触发器-->
     <name></name>
     <group></group>
  </delete-trigger>
</pre-processing-commands>

<prscessing-directives>
   <!--在计划作业和触发器上应该遵循的命令和原则-->
   <overwrite-existing-data>true or false</overwrite-existing-data>
   <!--是否覆写已经存在的任务计划数据,如果为false并且ignore-duplicates非false,那么文件中同名的触发器或作业将会继续存在,则会产生错误-->
   <ignore-duplicates>true or false</ignore-duplicates>
   <!--如果为true,计划中的任何同名的作业/触发器将会被忽略,不会产生错误-->
</processiong-directives>

<schedule>
   <job>
        <name>JobName</name>
        <group>JobGroup</group>
        <description></description>
        <job-class></job-class>
        <job-listener-ref></job-listener-ref>
        <!--volatility,durability,recover必须按顺序设定-->
        <volatility></volatility>
        <durability></durability>
        <recover></recover>
        <job-data-map>
            <!--entry可以设定多个-->
            <entry>
                <key></key>
                <value></value>
            <entry>
        </job-data-map>
   </job>

   <trigger>
      <!--Trigger分为simple,cron,date-interval三种类型,一个trigger中只能指定一种类型-->
      <simple> 
         <name></name>
         <group></group>
         <description></description>
         <job-name></job-name>
         <job-group></job-group>
         <calendar-name></calendar-name>
         <volatility></volatility>
         <job-data-map>
             <entrty>
                <key></key>
                <value></value>
             <entrty>
         </job-data-map>
         <start-time></start-time>
         <end-time></end-time>
         <misfire-instruction></misfile-instruction>
         <repeat-count></repeat-count>
         <repeat-interval></repeat-interval>
      </simple>   

      <cron>
         <name></name>
         <group></group>
         <description></description>
         <job-name></job-name>
         <job-group></job-group>
         <calendar-name></calendar-name>
         <volatility></volatility>
         <job-data-map>
             <entrty>
                <key></key>
                <value></value>
             <entrty>
         </job-data-map>
         <start-time></start-time>
         <end-time></end-time>
         <misfire-instruction></misfile-instruction>
         <cron-expression></cron-expression>
         <time-zone></time-zone>
      </cron>

      <date-interval>
         <name></name>
         <group></group>
         <description></description>
         <job-name></job-name>
         <job-group></job-group>
         <calendar-name></calendar-name>
         <volatility></volatility>
         <job-data-map>
             <entrty>
                <key></key>
                <value></value>
             <entrty>
         </job-data-map>
         <start-time></start-time>
         <end-time></end-time>
         <misfire-instruction></misfile-instruction>
         <repeat-interval></repeat-interval>
         <repeat-interval-unit></repeat-interval-unit>
      </date-interval>
    </trigger>
</schedule>
</job-scheduling-data>

代码清单7:quartz_jobs.xml示例

<?xmlversion="1.0"encoding="UTF-8"?>  
<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd" 
   version="1.8"> 
  
  <pre-processing-commands>
     <!--clear all jobs in scheduler-->
     <delete-jobs-in-group>*</delete-jobs-in-group>
     <!--delete all triggers in scheduler-->
     <delete-triggers-in-group>*</delete-triggers-in-group> 
  </pre-processing-commands>

  <processing-directives>
     <overwrite-existing-data>true</overwrite-existing-data>
     <ignore-duplicates>false</ignore-duplicates>
  </processing-directives>

  <schedule>
     <job>
        <name>helloQuartzJob</name>
        <group>DEFAULT</group>
        <description>简单的quartz使用</description>
        <job-class>HelloQuartzJob</job-class>
        <volatility>false</volatility>
        <durability>true</durability>
        <recover>false</recover>
     </job>

     <trigger>
        <cron>  
           <name>trigger</name>
           <group>DEFAULT</group>
           <job-name>helloQuartzJob</job-name>
           <job-group>DEFAULT</job-group> 
           <cron-expression>30/5 * * * * ?</cron-expression>
        </cron>
     </trigger>
  <schedule>
</job-scheduling-data>

代码清单8:Quartz任务调度

public class HelloQuartzScheduling{

   public static void main(String[] args)throws SchedulerException,ParseException{
    
     //创建调度工厂
     SchedulerFactory schedulerFactory=new StdSchedulerFactory();
     //创建调度
     Scheduler scheduler=schedulerFactory.getScheduler();
     //启动调度
     scheduler.start();
   } 
}

然后编译运行,但是可能会报出一个错误:

经检查发现,该异常的出现是缺少jta.jar这个包。只要从网上下载后,然后添加到classpath中去,那么就可以正常编译运行了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/coconutchen/blog/678600

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值