Quartz调度框架简单介绍

核心概念

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

下面的这些概念性的东西,可以看看,如果在其他网站了解过关于quartz调度的一个相关知识,那么下面的东西可以忽略!


调度器、任务和触发器这3个核心的概念

●Job:是一个接口,只有一个方法voidexecute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

●JobDetailQuartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

 

通过该类的构造函数可以更具体地了解它的功用:JobDetail(java.lang.String name,java.lang.String group, java.lang.Class

jobClass),该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称;

 

●Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTriggerCronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

●Calendarorg.quartz.Calendarjava.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。

 

假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。针对不同时间段类型,Quartzorg.quartz.impl.calendar包下提供了若干个Calendar的实现类,如AnnualCalendarMonthlyCalendarWeeklyCalendar分别针对每年、每月和每周进行定义;

 

●Scheduler:代表一个Quartz的独立运行容器,TriggerJobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中TriggerJobDetail

 

Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,JobTrigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;

 

●ThreadPoolScheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

 

Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。

正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job

如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。

 

Trigger自身也可以拥有一个JobDataMap,其关联的Job可以通过JobExecutionContext#getTrigger().getJobDataMap()获取Trigger中的JobDataMap。不管是有状态还是无状态的任务,在任务执行期间对TriggerJobDataMap所做的更改都不会进行持久,也即不会对下次的执行产生影响。

 

Quartz拥有完善的事件和监听体系,大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件。

一个Scheduler可以拥有多个Triger组和多个JobDetail组,注册TriggerJobDetail时,如果不显式指定所属的组,Scheduler将放入到默认组中,默认组的组名为Scheduler.DEFAULT_GROUP。组名和名称组成了对象的全名,同一类型对象的全名不能相同。

Scheduler本身就是一个容器,它维护着Quartz的各种组件并实施调度的规则。Scheduler还拥有一个线程池,线程池为任务提供执行线程——这比执行任务时简单地创建一个新线程要拥有更高的效率,同时通过共享节约资源的占用。通过线程池组件的支持,对于繁忙度高、压力大的任务调度,Quartz将可以提供良好的伸缩性。------------------------------------------------

 Quartz核心的概念:scheduler任务调度、Job任务、Trigger触发器、JobDetail任务细节

【Job任务:其实Job是接口,其中只有一个execute方法:
 package org.quartz;
public abstract interface Job
{
  public abstract void execute(JobExecutionContext paramJobExecutionContext)
    throws JobExecutionException;
}
我们开发者只要实现此接口,实现execute方法即可。把我们想做的事情,在execute中执行即可。

【JobDetail:任务细节,Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。

【Trigger触发器:执行任务的规则;比如每天,每小时等。
 一般情况使用SimpleTrigger,和CronTrigger,这个触发器实现了Trigger接口。
 对于复杂的时间表达式来说,比如每个月15日上午几点几分,使用CronTrigger
 对于简单的时间来说,比如每天执行几次,使用SimpleTrigger

【scheduler任务调度:是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。

【SchedulerFactory 则是 Scheduler 工厂,负责生成 Scheduler 。

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

一、声明式简单Demo

声明式:介绍一个我从网上摘录的已经测试成功的例子!大家可以根据下面的例子自己进行扩展!

【例子说明:例子是根据指定的目录和指定的后缀名字,列举出目录下面所有的指定的后缀名的文件!

【文件结构:



【quartz.properties

#============================================================================

# Configure Main SchedulerProperties 

#============================================================================

org.quartz.scheduler.instanceName = TestScheduler

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.triggHistory.class= org.quartz.plugins.history.LoggingJobHistoryPlugin

org.quartz.plugin.jobInitializer.class= org.quartz.plugins.xml.JobInitializationPlugin

org.quartz.plugin.jobInitializer.fileNames= jobs.xml

org.quartz.plugin.jobInitializer.overWriteExistingJobs= true

org.quartz.plugin.jobInitializer.failOnFileNotFound= true

org.quartz.plugin.jobInitializer.scanInterval= 10

# org.quartz.plugin.jobInitializer.wrapInUserTransaction = false


【jobs.xml

<?xml version='1.0' encoding='utf-8'?>

<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData

 http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"

  version="1.5">

 <job>

   <job-detail>

    <name>ScanDirectory</name>

    <group>DEFAULT</group>

    <description>

          A job that scans a directory forfiles

    </description>

    <job-class>

            com.cmcc.maven.quartz.ScanDirectoryJob

    </job-class>

    <volatility>false</volatility>

    <durability>false</durability>

    <recover>false</recover>

    <job-data-map allows-transient-data="true">

         <entry>

         <key>SCAN_DIR</key>

         <value>D:\conf1</value>

       </entry>

    </job-data-map>

   </job-detail>

     

   <trigger>     

    <simple>     

       <name>scanTrigger</name>     

       <group>DEFAULT</group>     

       <job-name>ScanDirectory</job-name>     

       <job-group>DEFAULT</job-group>     

       <start-time>2008-09-03T14:43:00</start-time>     

       <!-- repeat indefinitely every 10 seconds -->     

       <repeat-count>-1</repeat-count>     

       <repeat-interval>10000</repeat-interval>

    </simple>

   </trigger>     

 </job>     

</quartz>


【SimpleScheduler.java

package com.cmcc.maven.quartz;

 

import java.util.Date;      

 

import org.apache.commons.logging.Log;      

importorg.apache.commons.logging.LogFactory;      

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.impl.StdSchedulerFactory;

 

public class SimpleScheduler{

   static Log logger = LogFactory.getLog(SimpleScheduler.class);

   public  void main() {

       SimpleScheduler simple = new SimpleScheduler();

       try {

           // Create a Scheduler and schedule the Job

           Scheduler scheduler = simple.createScheduler();

           // Jobs can be scheduled after Scheduler is running

           scheduler.start();

           logger.info("Scheduler started at " + new Date());

      } catch (SchedulerException ex) {

           logger.error(ex);

      }

   }

   public Scheduler createScheduler() throws SchedulerException

   {//创建调度器

       return StdSchedulerFactory.getDefaultScheduler();

    }

}


【ScanDirectoryJob.java

package com.cmcc.maven.quartz;

import java.io.File;      

import java.io.FileFilter;

import java.util.Date;      

     

import org.apache.commons.logging.Log;      

importorg.apache.commons.logging.LogFactory;      

import org.quartz.Job;      

import org.quartz.JobDataMap;      

import org.quartz.JobDetail;      

import org.quartz.JobExecutionContext;      

importorg.quartz.JobExecutionException; 

 

public class ScanDirectoryJobimplements Job {

   static Log logger = LogFactory.getLog(ScanDirectoryJob.class);//日志记录器

   

   public void execute(JobExecutionContext context) throws JobExecutionException{

       //Every job has its own job detail

       JobDetail jobDetail = context.getJobDetail();

       // The name is defined in the job definition

       String jobName = jobDetail.getName();//任务名称

       // Log the time the job started

       logger.info(jobName + " fired at " + new Date());//记录任务开始执行的时间

       // The directory to scan is stored in the job map

       JobDataMap dataMap = jobDetail.getJobDataMap();//任务所配置的数据映射表

       String dirName = dataMap.getString("SCAN_DIR");//获取要扫描的目录

       // Validate the required input      

       if (dirName == null) {

                //所需要的扫描目录没有提供

            throw new JobExecutionException( "Directory not configured" );

       }

       // Make sure the directory exists

       File dir = new File(dirName);

       if (!dir.exists()) {

                //提供的是错误目录

           throw new JobExecutionException( "Invalid Dir "+ dirName);

       }

       // Use FileFilter to get only XML files

       FileFilter filter = new FileExtensionFileFilter(".xml");

       //只统计xml文件

       File[] files = dir.listFiles(filter);

       if (files == null || files.length <= 0) {

                //目录下没有xml文件

           logger.info("No XML files found in " + dir);

           // Return since there were no files

           return;

       }

       // The number of XML files

       int size = files.length;

       // Iterate through the files found

       for (int i = 0; i < size; i++) {

           File file = files[i];

           // Log something interesting about each file.

           File aFile = file.getAbsoluteFile();

           long fileSize = file.length();

           String msg = aFile + " - Size: " + fileSize;

           logger.info(msg);//记录下文件的路径和大小

           System.out.println("==============================");

           System.out.println(msg);

       }

    }

}


【FileExtensionFileFilter.java

package com.cmcc.maven.quartz;

import  java.io.File;       
import  java.io.FileFilter;   


public class FileExtensionFileFilter implements  FileFilter 
{
     private  String extension;//文件后缀     
     
     public  FileExtensionFileFilter(String extension)
     {       
         this.extension = extension;
     }             
     public   boolean  accept(File file)
     {   //只接受指定后缀的文件       
         // Lowercase the filename for easier comparison       
         String lCaseFilename = file.getName().toLowerCase();//小写化
         return  (file.isFile() &&(lCaseFilename.indexOf(extension) >  0 )) ?  true : false ;       
     }       
}


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

二、编程式简单Demo


import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

public classSimpleJob implementsJob {

   //①实例Job接口方法

   public voidexecute(JobExecutionContext jobCtx)throwsJobExecutionException {

      System.out.println("测试!"+ SimpleTriggerRunner.i);

      SimpleTriggerRunner.i++;

   }

}

上面这个类用一条非常简单的输出语句实现了Job接口的execute(JobExecutionContextcontext) 方法,这个方法可以包含想要执行的任何代码。

下面,我们通过SimpleTriggerSimpleJob进行调度:

代码清单2 SimpleTriggerRunner:使用SimpleTrigger进行调度

import java.util.Date;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.SimpleTrigger;

import org.quartz.impl.StdSchedulerFactory;

public classSimpleTriggerRunner {

   public static int i= 0;

   public static void main(String args[]) {

      try {

         //①创建一个JobDetail实例,指定SimpleJob

         JobDetailjobDetail= newJobDetail("job1_1","jGroup1",SimpleJob.class);

         //②通过SimpleTrigger定义调度规则:马上启动,每2秒运行一次,共运行10

         SimpleTriggersimpleTrigger= newSimpleTrigger("trigger1_1","tgroup1");

         simpleTrigger.setStartTime(new Date());

         simpleTrigger.setRepeatInterval(2000);

         simpleTrigger.setRepeatCount(10);

         //③通过SchedulerFactory获取一个调度器实例

         SchedulerFactoryschedulerFactory= newStdSchedulerFactory();

         Schedulerscheduler= schedulerFactory.getScheduler();

         scheduler.scheduleJob(jobDetail, simpleTrigger);//④ 注册并进行调度

         scheduler.start();//⑤调度启动

         }catch(Exception e) {

            e.printStackTrace();

         }

      }

}

首先在处通过JobDetail封装SimpleJob,同时指定JobScheduler中所属组及名称,这里,组名为jGroup1,而名称为job1_1

处创建一个SimpleTrigger实例,指定该TriggerScheduler中所属组及名称。接着设置调度的时间规则。

最后,需要创建Scheduler实例,并将JobDetailTrigger实例注册到Scheduler中。这里,我们通过StdSchedulerFactory获取一个Scheduler实例,并通过scheduleJob(JobDetailjobDetail, Trigger trigger)完成两件事:

1)JobDetailTrigger注册到Scheduler中;

2)Trigger指派给JobDetail,将两者关联起来。

Scheduler启动后,Trigger将定期触发并执行SimpleJobexecute(JobExecutionContextjobCtx)方法,然后每 10 秒重复一次,直到任务被执行 100 次后停止。

还可以通过SimpleTriggersetStartTime(java.util.Date startTime)setEndTime(java.util.Date endTime)指定运行的时间范围,当运行次数和时间范围冲突时,超过时间范围的任务运行不被执行。如可以通过simpleTrigger.setStartTime(new Date(System.currentTimeMillis() + 60000L))指定60秒钟以后开始。

除了通过scheduleJob(jobDetail,simpleTrigger)建立TriggerJobDetail的关联,

还有另外一种关联TriggerJobDetail的方式:

JobDetail jobDetail = new JobDetail("job1_1","jGroup1", SimpleJob.class);

SimpleTrigger simpleTrigger = new SimpleTrigger("trigger1_1","tgroup1");



simpleTrigger.setJobGroup("jGroup1");①-1:指定关联的Job组名

simpleTrigger.setJobName("job1_1");①-2:指定关联的Job名称

scheduler.addJob(jobDetail, true);② 注册JobDetail

scheduler.scheduleJob(simpleTrigger);③ 注册指定了关联JobDetail的Trigger

在这种方式中,Trigger通过指定Job所属组及Job名称,然后使用SchedulerscheduleJob(Triggertrigger)方法注册Trigger。有两个值得注意的地方:

通过这种方式注册的Trigger实例必须已经指定Job组和Job名称,否则调用注册Trigger的方法将抛出异常;

引用的JobDetail对象必须已经存在于Scheduler中。也即,代码中的先后顺序不能互换。

在构造Trigger实例时,可以考虑使用org.quartz.TriggerUtils工具类,该工具类不但提供了众多获取特定时间的方法,还拥有众多获取常见Trigger的方法,如makeSecondlyTrigger(String trigName)方法将创建一个每秒执行一次的Trigger,而makeWeeklyTrigger(StringtrigName, int dayOfWeek, int hour, int minute)将创建一个每星期某一特定时间点执行一次的Trigger。而getEvenMinuteDate(Datedate)方法将返回某一时间点一分钟以后的时间。


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

四、cron表达式详解

cron 表达式是指定定时任务的很强大的一个表达式

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: 
Seconds Minutes Hours DayofMonth Month DayofWeek Year或 
Seconds Minutes Hours DayofMonth Month DayofWeek

每一个域可出现的字符如下: 
Seconds:可出现", - * /"四个字符,有效范围为0-59的整数 
Minutes:可出现", - * /"四个字符,有效范围为0-59的整数 
Hours:可出现", - * /"四个字符,有效范围为0-23的整数 
DayofMonth:可出现", - * / ? L W C"八个字符,有效范围为0-31的整数 
Month:可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc 
DayofWeek:可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推 
Year:可出现", - * /"四个字符,有效范围为1970-2099年


每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是: 
(1)*:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。


(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。 

(3)-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次 

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次. 

(5),:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。 

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。 

(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。 

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

举几个例子: 
0 0 2 1 * ? * 表示在每月的1日的凌晨2点调度任务 
0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业 
0 15 10 ? 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。 
按顺序依次为 
秒(0~59) 
分钟(0~59) 
小时(0~23) 
天(月)(0~31,但是你需要考虑你月的天数) 
月(0~11) 
天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT) 
年份(1970-2099)

其中每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?

0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 
0 0 12 ? * WED 表示每个星期三中午12点 
"0 0 12 * * ?" 每天中午12点触发 
"0 15 10 ? * *" 每天上午10:15触发 
"0 15 10 * * ?" 每天上午10:15触发 
"0 15 10 * * ? *" 每天上午10:15触发 
"0 15 10 * * ? 2005" 2005年的每天上午10:15触发 
"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 
"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发 
"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 
"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发 
"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 
"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 
"0 15 10 15 * ?" 每月15日上午10:15触发 
"0 15 10 L * ?" 每月最后一日的上午10:15触发 
"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

有些子表达式能包含一些范围或列表

例如:子表达式(天(星期))可以为 “MON-FRI”,“MON,WED,FRI”,“MON-WED,SAT”

“*”字符代表所有可能的值

因此,“*”在子表达式(月)里表示每个月的含义,“*”在子表达式(天(星期))表示星期的每一天

“/”字符用来指定数值的增量 
例如:在子表达式(分钟)里的“0/15”表示从第0分钟开始,每15分钟 
在子表达式(分钟)里的“3/20”表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样

“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值 
当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”

“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写 
但是它在两个子表达式里的含义是不同的。 
在天(月)子表达式中,“L”表示一个月的最后一天 
在天(星期)自表达式中,“L”表示一个星期的最后一天,也就是SAT

如果在“L”前有具体的内容,它就具有其他的含义了

例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五 
注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题

字段 允许值 允许的特殊字符 
秒 0-59 , - * / 
分 0-59 , - * / 
小时 0-23 , - * / 
日期 1-31 , - * ? / L W C 
月份 1-12 或者 JAN-DEC , - * / 
星期 1-7 或者 SUN-SAT , - * ? / L C # 
年(可选) 留空, 1970-2099 , - * /

校验输入的cron表达式格式是否正确:
package com.test.cron;

public class CheckCron {

String  ss = "";

public static void main(String [] args) {
String[]  ss = {"60 0 10,14,16 * * ?",
"01 0/30 9-17 * * ?",// 朝九晚五工作时间内每半小时 
"80 0 12 ? * WED",// 表示每个星期三中午12点 
"00 0 12 * * ?",// 每天中午12点触发 
"0 65 10 ? * *",// 每天上午10:15触发 
"0 05 10 * * ?",// 每天上午10:15触发 
"0 85 10 * * ? *",// 每天上午10:15触发 
"0 00 10 * * ? 2005",// 2005年的每天上午10:15触发 
"0 * 24 * * ?",// 在每天下午2点到下午2:59期间的每1分钟触发 
"0 0/5 23 * * ?",//在每天下午2点到下午2:55期间的每5分钟触发 
"0 0/5 00,18 * * ?",// 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 
"0 0-5 04 * * ?",// 在每天下午2点到下午2:05期间的每1分钟触发 
"0 10,44 14 ? 3 WED",// 每年三月的星期三的下午2:10和2:44触发 
"0 15 10 ? * MON-FRI",// 周一至周五的上午10:15触发 
"0 15 10 15 * ?",// 每月15日上午10:15触发 
"0 15 10 L * ?",// 每月最后一日的上午10:15触发 
"0 15 10 ? * 6L",// 每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6L 2002-2005",// 2002年至2005年的每月的最后一个星期五上午10:15触发 
"0 15 10 ? * 6#3"// 每月的第三个星期五上午10:15触发
};
for (int i = 0 ;i<ss.length;i++) {
boolean s = org.quartz.CronExpression.isValidExpression(ss[i]);//用来判断所指定的cron表达式是否符合规则
System.out.println("【"+ ss[i]+"】的判断结果是:"+s);
}
}
}

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

五、SSM(maven)框架里面使用调度框架的例子

下面的例子是使用调度框架代替定时器,执行定时任务,大家可以参考例子,有什么不懂得可以问我!

这个例子的好处是比定时器使用起来更加方便

————————————————————————

【application-schedule.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
   http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
   http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<description>Spring scheduling 配置 </description>

<bean id="keywords" class="com.cmcc.bdp.common.util.IndicesUtil" />
<bean id="quartzRefreshKeywords" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="keywords" />
<property name="targetMethod" value="refreshKeywords" />
</bean>
<bean id="t_quartzRefreshKeywords" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="quartzRefreshKeywords" />
<property name="cronExpression" value="${keywords.time}" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="t_quartzRefreshKeywords" />
</list>
</property>
</bean>
</beans>

————————————————————————

【application-schedule.properties

# cron表达式

keywords.time=0 */2 * * * ?

————————————————————————

【application-context.xml

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
   http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
   http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<description>Spring公共配置 </description>

<!-- 定义受环境影响易变的变量 -->
<context:property-placeholder location="classpath*:/application.properties" ignore-unresolvable="true" />
<context:property-placeholder location="classpath*:/application-schedule.properties" ignore-unresolvable="true" />

<!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
<context:component-scan base-package="com.cnn" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>
</beans> 

————————————————————————

【web.xml:

    <!-- 指定Spring配置文件的路径 -->

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>classpath*:/application*.xml</param-value>

    </context-param>

    <!--SpringApplicationContext 载入 -->

    <!-- 设置Spring的监听,项目启动时候初始化 -->

    <listener>

        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

————————————————————————

SimpleJob.java

package com.cmcc.maven.quartz; 

public classSimpleJob{//没有实现Job

   //①实例Job接口方法

   public void refreshKeywords(){

      System.out.println("定时调度测试!");

   }

}

————————————————————————

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



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






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值