前言
开通博客的目的主要是用来记录小组成员的学习情况,而作为小组里资历经验最浅的我被叫来写学习笔记我的内心其实是崩溃的。作为刚开始工作的小菜比,能想到的会能做的会比较浅写这个笔记的原因主要是记录学习的点滴和成长,如果在理解上面有错误欢迎指教(求各路大神轻喷)...
公司使用的是quartz 1.8.6版本,在使用上与最新的版本会存在一些区别,具体介绍将在今后不知道什么时候进行讨论。
quartz
Quratz主要分三大部分:调度器scheduler、任务job,jobDetail、触发器trigger。
一、触发器:
sampleTrigger:单纯按照时间间隔执行任务
cronTrigger(cron格林日历)按照cron格式执行任务
1.SampleTrigger
在简单触发器中可以规定触发器的名字、分组、任务名称、任务分组、开始时间、结束时间、执行次数和执行任务的间隔时间。下为SampleTrigger实例:
1 SimpleTrigger simpleTrigger=new SimpleTrigger("Trigger1",new Date()); 2 simpleTrigger.setRepeatInterval(1000);//每秒运行一次 3 simpleTrigger.setRepeatCount(50);//运行50次
2.CronTrigger
CronTrigger实例的配置需要cron表达式。一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。当设置元素冲突时需要通过设置一个问号“?”来表明不想设置的那个字段,“/”表示重复次数(10/6表示每10秒重复6次)
|
* |
* |
* |
* |
* |
* |
* |
|
秒 |
分 |
小时 |
日 |
月 |
星期 |
年 |
Cron表达式范例:
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
每周星期天凌晨1点实行一次:0 0 1 ? * L
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
下为CronTrigger实例:
CronTrigger cronTrigger=new CronTrigger("CronT1", "Crongroup", "*/1 * * * * ?");
二、任务:
任务类应该是一个实现了Job接口的具体类。在任务中需要实现execute方法,方法的原型为:public void execute(JobExecutionContext context) throws JobExecutionException{}
这个方法用来接收对象JobExecutionContext,它为任务实例提供了运行环境,里面封装了调度器信息,触发器信息,任务信息及其他信息。JobExecutionContext 被用来访问 JobDetail 类,JobDetail 类持有任务的详细信息,包括为任务实例指定的名称,任务所在的任务组,是否被持久化等等。
在对任务进行调度的时候需要将将所定义的任务(Job)绑定到JobDetail:
JobDetail jobDetail=new JobDetail (任务名,任务类... );
任务也可以通过execute()获得自定义的信息,比如执行发送邮件是获得邮箱地址、扫描文件是获得扫描目录等等。虽然可以通过硬编码定义这些信息但是却难以复用,因此推荐通过JobDataMap来实现。上文提到JobExecution访问JobDetail来获得信息,而JobDetail又会关联一个JobDataMap,对任务的自定义信息可以定义在调度类中,在任务类execute()中便可以通过 #JobExecutionContext.getJobDetail().getJobDataMap().getString("key");获得信息了。但是这样做的缺点是JobDataMap只能保存被序列化的对象否则容易出现类型转化问题。比较常用的如java.sql.Connection,虽然无法放入JobDataMap中,但是可以存放在SchedulerContext中:
scheduler.getContext().put("java.sql.Connection",connection);
这样在execute()中也可以获得:
#JobExecutionContext.getScheduler().getContext().get("java.sql.Connection");
三、调度器:
启动调度器时需要先对调度器进行实例化,调度器启动后一旦被关闭(shutdown)就不能重新启动除非重新实例化它:
SchedulerFactory schedulerFactory=new StdSchedulerFactory(); Scheduler scheduler=schedulerFactory.getScheduler(); scheduler.start();
将任务纳入进程(需要获得任务和触发器):
scheduler.scheduleJob(#jobDetail,#Trigger);
或者:
scheduler.addJob(#jobDetail, true); scheduler.scheduleJob(#trigger);
这里可以看到scheduleJob()有两个构造函数,其中一个只有触发器参数,这给了我一个疑问,既然可以在构造器中指定任务的名称和任务组是否就表示在构造器中绑定任务就不需要告知调度器相应任务了呢?为此我进行了实验,我将任务命名为Job1,并分配到了jobgroup组中:
JobDetail jobDetail=new JobDetail("Job1","Group1",HelloQuartzJob.class); SimpleTrigger simTrigger=new SimpleTrigger("t1", "tg1", "Job1", "jobgroup", new Date(), null, 50, 1000);
结果是scheduler告诉我他找不到这个任务。我的理解是说schedule不会通过触发器寻找任务,只会检查调度器的任务名称和分组是否匹配。
附:关于quartz的初级使用请看如下代码:
任务类:
//创建任务 public class HelloQuartzJob implements Job { public void execute(JobExecutionContext content) throws JobExecutionException{ System.out.println("hello quartz,it runs at "+ new Date()+" by "+content.getTrigger().getGroup()+"job"+content.getJobDetail().getGroup()+"count"+content.getRefireCount()); } }
简单触发:
public class HelloSimTriQuratz { public static void main(String[] args) throws Exception { //实例化调度器 SchedulerFactory schedulerFactory=new StdSchedulerFactory(); Scheduler scheduler=schedulerFactory.getScheduler(); scheduler.start(); //绑定任务 JobDetail detail=new JobDetail("Job1","jobgroup" ,HelloQuartzJob.class); JobDetail detail2=new JobDetail("Job2","jobgroup" ,HelloQuartzJob2.class); //设置任务的执行方式 //SimpleTrigger simpleTrigger=new SimpleTrigger("Trigger1", new Date()); SimpleTrigger simTrigger=new SimpleTrigger("t1", "tg1", "Job1", "jobgroup", new Date(), null, 50, 1000); scheduler.addJob(detail, true); scheduler.addJob(detail2, true); scheduler.scheduleJob(simTrigger); }
格林日历触发:
public class HelloCronTriQuartz { public static void main(String[] args) throws SchedulerException { SchedulerFactory schedulerFactory=new StdSchedulerFactory(); Scheduler scheduler=schedulerFactory.getScheduler(); scheduler.start(); JobDetail jobDetail=new JobDetail("Job1",HelloQuartzJob.class); try { CronTrigger cronTrigger=new CronTrigger("CronT1", "Crongroup", "*/1 * * * * ?"); scheduler.scheduleJob(jobDetail,cronTrigger); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
quartz整合SpringMvc
在Spring的配置文件中添加如下代码(详细信息看注释),然后定义任务就可以使用了。
<bean id="SpringJob" class="com.job.SpringJob"/> <!-- 引用任务描述 --> <bean id="methodInvokingJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject"> <!-- 引用任务方法所在的类 --> <ref bean="SpringJob" /> </property> <property name="targetMethod"> <!-- 指定任务方法名称 --> <value>work</value> </property> </bean> <!-- 配置触发器 --> <bean id="SpringTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <!-- 这里不可以直接在属性jobDetail中引用taskJob,因为他要求的是一个jobDetail类型的对象,所以我们得通过MethodInvokingJobDetailFactoryBean来转一下 --> <property name="jobDetail"> <!-- 引用任务描述bean --> <ref bean="methodInvokingJobDetail" /> </property> <!-- 每3秒触发一次,详情见上文cron表达式 --> <property name="cronExpression"> <!-- 秒 分 时 日 月 周 年 --> <value>0/3 * * * * ?</value> </property> </bean> <!-- 配置任务工厂, 只能有一个这种bean --> <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <!-- 添加触发器 --> <property name="triggers"> <list> <ref local="SpringTrigger" /> <!-- ... --> </list> </property> </bean>