Quartz学习笔记

什么是job schedule system?

job schedule system是负责在预定义的时间执行或者通知某个应用组件的系统。举个例子来说,比如在每周一早上9:30发送email通知客户最新的业务情况。

java.util.Timer和java.util.TimerTask

Timer和TimerTask是可以完成job schedule的两个jdk提供的类,不过这不能称为一个system。Timer和TimerTask是很简单的,不直接支持持久化任务,线程池和类似日历(calendar-like)的计划安排,在完成一些高级功能上开发人员要进行大量的扩展。

Quartz的简单介绍

Quartz是opensymphony组织专攻job scheduling领域又一个开源利器,可以到http://www.opensymphony.com/quartz查看详细信息。Quartz是轻量级的组件,开发人员只需要加载单独的jar包就可以利用Quartz强大的日程安排功能。当然,假如你为Quartz配备了数据库持久化任务的特性,Quartz也可以很好的利用这一点,从而在机器重启后还能够记住你原先安排的计划。

Quartz中我们接触最多的接口使Scheduler接口,该接口的提供了计划安排的功能,比如schedule/unschedule计划、start/pause/stop Scheduler.

Quartz提供一些常用的Listener(JobListener,TriggerListener,SchedulerListener)用于完全的监视计划安排和执行情况。

开始我们的Quartz之旅

·  HelloWorld example:

想必大家很想看一个HelloWorld的例子了吧,那么还是以HellowWorld开始。

import java.util.Date;

import org.quartz.Job;

import org.quartz.JobDetail;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.quartz.Scheduler;

import org.quartz.SchedulerFactory;

import org.quartz.Trigger;

import org.quartz.impl.StdSchedulerFactory;

/**

* @author snowway

* @version $Id$

*/

public class SayHelloWorldJob implements Job{

/*

* (non-Javadoc)

*

* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)

*/     public void execute(JobExecutionContext context) throws JobExecutionException{

System.out.println("hello world!");

}

public static void main(String[] args) throws Exception{

SchedulerFactory factory = new StdSchedulerFactory();

Scheduler scheduler = factory.getScheduler();

JobDetail jobDetail = new JobDetail("SayHelloWorldJob",

Scheduler.DEFAULT_GROUP,

SayHelloWorldJob.class);

Trigger trigger = new SimpleTrigger("SayHelloWorldJobTrigger",

Scheduler.DEFAULT_GROUP,

new Date(),

null,

0,

0L);

scheduler.scheduleJob(jobDetail, trigger);

scheduler.start();

}

}

为了简单起见,我把main方法写在SayHelloWorldJob中了,执行SayHelloWorldJob可以看到控制台打印hello world.

·  回顾Hello World example:

Job是什么?

接口Job是每个业务上需要执行的任务需要实现的接口,该接口只有一个方法:

package org.quartz;

public interface Job {

public void execute(JobExecutionContext context)

throws JobExecutionException;

}

execute方法也就是当时间到达后,Quartz回调的方法,我们使SayHelloWorldJob实现Job接口以提供打印功能

JobDetail是什么? JobDetail描述了一个任务具体的信息,比如名称,组名等等。

JobDetail jobDetail = new JobDetail("SayHelloWorldJob",

Scheduler.DEFAULT_GROUP,

SayHelloWorldJob.class);

在上面的构造方法中,第一个是任务的名称,第二个是组名,第三个就是实际当任务需要执行的回调类。

Trigger是什么?

Trigger顾名思义就是触发器,Quartz有个很好的想法就是分离了任务和任务执行的条件。Trigger就是控制任务执行条件的类,当Trigger认为执行条件满足的时刻,Trigger会通知相关的Job去执行。分离的好处是:

1.你可以为某个Job关联多个Trigger,其中任何一个条件满足都可以触发job执行,这样可以完成一些组合的高级触发条件

2.当Trigger失效后(比如:一个永远都不能满足的条件),你不必去声明一个新的job,代替的是你可以为job关联一个新的Trigger让job可以继续执行。

目前的Quartz实现中,存在两种Trigger,SimpleTrigger和CronTrigger,SimpleTrigger用来完成一些比如固定时间执行的任务,比如:从现在开始1分钟后等等;而CronTrigger(没错,和unix的cron进程的含意一样)用来执行calendar-like的任务,比如:每周五下午3:00,每月最后一天等等。

Trigger trigger = new SimpleTrigger("SayHelloWorldJobTrigger",

Scheduler.DEFAULT_GROUP,

new Date(),

null,

0,

0L); 这个构造方法中,第一个是Trigger的名称,第二个是Trigger的组名,第三个是任务开始时间,第四个是结束时间,第五个是重复

次数(使用SimpleTrigger.REPEAT_INDEFINITELY常量表示无限次),最后一个是重复周期(单位是毫秒),那么这样就创建

了一个立刻并只执行一次的任务。

scheduler.scheduleJob(jobDetail, trigger);

这条语句就是把job和Trigger关联,这样当Trigger认为应该触发的时候就会调用(实际上是Scheduler调用)job.execute方法了。

scheduler.start();

千万别忘了加上上面的语句,这条语句通知Quartz使安排的计划生效。

关于execute方法的参数JobExecutionContext

JobExecutionContext就和很多Context结尾的类功能一样,提供的运行时刻的上下文环境,JobExecutionContext中有

Scheduler,JobDetail,Trigger等很多对象的引用,从而当你在execute方法内部须需要这些对象的时刻提供的便利。

JobDetail和Trigger的name和group Scheduler实例对应了很多job和trigger的实例,为了方便的区分,Quartz使用name和group这两个特性,正如你想向的一样,

同一个group下不能有两个相同name的JobDetail,Trigger同理

同一个Scheduler下不能有两个相同group的JobDetail,Trigger同理

JobDetail和Trigger的完全限定名为:group + name

·  更深入的思考...

HelloWorld的例子还不足以说明一些问题,一些人可能会这样问:假如execute方法中需要一些额外的数据怎么办?比如说execute

中希望发送一封邮件,但是我需要知道邮件的发送者、接收者等信息?

存在两种解决方案:

1.JobDataMap类:

每个JobDetail都关联了一个JobDataMap实例,JobDataMap是java.util.Map的子类,基本上是提供key-value形式的数据,并提供了一些便利方法(主要是对java基本数据类型的支持,如put(String key,int value)),当开发人员创建JobDetail的时候,可以把附加信息放到JobDataMap中,那么在execute方法中可以根据key找到需要的值。

JobDetail job = new JobDetail....

job.getJobDataMap().put("from","[email protected]");

...

在execute中

String from = jobExecutionContext.getJobDetail().getJobDataMap().getString("from");

....

不过,当你使用数据库存储JobDetail的时候(默认情况下使用RAM),这里有一个致命的弱点,你不能把没有实现java.io.Serializable的对象放入JobDataMap中,因为Quartz将使用Blob字段保存(也可以通过配置文件关闭)序列化过的JobDataMap中的对象。比如你在execute方法中需要一个java.sql.Connection接口实例,这种情况也是普遍的,那么通常情况下你不能把Connection放入JobDataMap,即使你只想在execute中使用。(注:读者可暂时认为上面这段话是正确的,然而可以通过指示quartz改变这种行为,那属于高级话题)

2.假如你需要一个java.sql.Connection,用于在execute中完成某些操作,那么你可以把Connection放入Quartz的SchedulerContext中,execute也可以访问,并且Quartz不会持久化SchedulerContext中的任何东西。

scheduler.getContext().put("java.sql.Connection",connection);

execute中

Connection con = (Connection)jobExecutionContext.getScheduler().getContext().get("java.sql.Connection");

Java 中已经有一个 timer 类可以用来进行执行计划,定时任务。我们所要做的只是 继承 java.util.TimerTask 类。如下所示:

package com.yourcompany.scheduling;

import java.util.Calendar;

import java.util.Date;

import java.util.Timer;

import java.util.TimerTask;

public class ReportGenerator extends TimerTask {

public void run() {

System.out.println("Generating report");

//TODO generate report

}

}

class MainApplication {

public static void main(String[] args) {

Timer timer new Timer();

Calendar date = Calendar.getInstance();

date.set(

Calendar.DAY_OF_WEEK,

Calendar.SUNDAY

);

date.set(Calendar.HOUR, 0);

date.set(Calendar.MINUTE, 0);

date.set(Calendar.SECOND, 0);

date.set(Calendar.MILLISECOND, 0);

// Schedule to run every Sunday in midnight

timer.schedule(

new ReportGenerator(),   // TimerTask

date.getTime(),          // Timer

1000 * 60 * 60 * 24 * 7   // delay

);

}

}

这里有几个问题,我们的类继承了TimerTask ,而timerTask 也是实现了 java.lang.Runnable 接口。我们所要做的只是在我们自己的类里重置 run() 方法。所以我们的TimerTask类其实是一种线程,但线程的调度往往不是按照我们希望来实现的,因为一些垃圾收集等原因,我们计划的时间点,却没有执行必要的任务。这样会产生一些问题。虽然,Timer 类也提供了scheduleAtFixedRate() 方法用来在垃圾收集后能够快速的追上任务进度,但这个不一定是我们所需要的。特别是在 一些 J2EE 服务器上 Timer 是无法控制的,因为它不在容器的权责范围内。另外的,这个任务调度也缺乏一些企业级所需要的 特殊 日期定制的功能,以及修改,查找任务的功能。

这里我们要介绍的是一个开源项目:Quartz 。

Quartz 定义了两种 基本接口 Job 和 Trigger 。 看名字也就知道,我们的任务必须实现 Job, 我们的时间触发器定义在 Trigger 内。 看一个例子也许能更快的了解他的使用方法: package com.yourcompany.scheduling;

import org.quartz.*;

public class QuartzReport implements Job {

public void execute(JobExecutionContext cntxt) //必须实现的方法

throws JobExecutionException {

System.out.println(

"Generating report - " +

cntxt.getJobDetail().getJobDataMap().get("type")

);

//TODO Generate report

}

public static void main(String[] args) {

try {

SchedulerFactory schedFact

new org.quartz.impl.StdSchedulerFactory();

Scheduler sched = schedFact.getScheduler();

sched.start();

JobDetail jobDetail =

new JobDetail(

"Income Report",       // 任务名

"Report Generation", // 任务组

QuartzReport.class    //任务执行的类

);

jobDetail.getJobDataMap().put(

"type",

"FULL"

);

CronTrigger trigger new CronTrigger(

"Income Report",              //触发器名

"Report Generation"          //触发器组

);

trigger.setCronExpression(     // 触发器时间设定

"0 0 12 ? * SUN"

);

sched.scheduleJob(jobDetail, trigger); // 执行任务

} catch (Exception e) {

e.printStackTrace();

}

}

}

这里面我们可以看到,当我们定义了任务执行 QuartzReport 类后,需要定一个Scheduler类用来执行计划任务。

一个JobDetail 类来描述这个任务的信息,包括任务信息,任务所在组,任务执行的类。

然后还要定义一个 触发器,类似的也包括触发器名,触发器所在组,触发器触发时间设定。

最后是调度器Scheduler类执行计划任务。基本上一个计划任务执行的流程就完成了。

当然,我们还看到了上面红色代表的内容,这些内容主要是提供在job方法执行的时候所需要的参数的提供。这里使用了JobDataMap 类,它其实就是实现了map的特殊应用的一个类,使用方法与Map 很相似。我们可以用 put() 输入参数。在Job类中使用cntxt.getJobDetail().getJobDataMap().get("type") 方法获取输入的参数的值。这里的cntxt 是JobExecutionContext 。就是包含任务执行上下文的一个信息类。这样我们的一个基本的任务执行就可以搞定了。

触发器有两类:SimpleTrigger andCronTrigger. 。SimpleTrigger主要提供了跟 java.util.Timer 类相似的功能.。你可以在里面定义 任务的起始时间,终止时间,任务的执行次数,任务执行的中间间隔 。 而 CronTrigger类主要提供了更高级的任务调度时间设置,例如 每个星期天的早上7点  。CronTrigger的时间设置说明在最后来介绍。

下面我们介绍一下在 J2EE 环境下如何来使用 Quartz 。

首先,我们要配置 web.xml ,添加 一下内容,主要是Quartz 的初始化,

<servlet>

<servlet-name>QuartzInitializer</servlet-name>

<display-name>Quartz Initializer Servlet</display-name>

<servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

然后还要有一个Quartz 的配置文件 quartz.properties 放置在 WEB-INF/classes目录下面。StdScheduleFactory()会读取它。配置如下 #

# Configure Main Scheduler Properties

#

org.quartz.scheduler.instanceName = TestScheduler

org.quartz.scheduler.instanceId = one

#

# Configure ThreadPool

#

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 5

org.quartz.threadPool.threadPriority = 4

#

# Configure JobStore

#

org.quartz.jobStore.misfireThreshold = 5000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

这里我们使用的 RAMJobStore 存储方式,这样如果我们的web服务器重启的话,我们所有未执行的任务信息都回丢失。当然,我们也有另外的选择,我们可以把这样的信息存储在数据库内,就是使用JDBCJobStoreTX

#

# Configure ThreadPool

#

org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

org.quartz.jobStore.dataSource = myDS

org.quartz.jobStore.tablePrefix = QRTZ_

#

# Configure Datasources

#

org.quartz.dataSource.myDS.driver = org.postgresql.Driver

org.quartz.dataSource.myDS.URL = jdbc:postgresql:dev

org.quartz.dataSource.myDS.user = dejanb

org.quartz.dataSource.myDS.password =

org.quartz.dataSource.myDS.maxConnections 5

附:cronExpression配置说明


字段


允许值


允许的特殊字符



0-59


, - * /



0-59


, - * /


小时


0-23


, - * /


日期


1-31


, - * ? / L W C


月份


1-12 或者 JAN-DEC


, - * /


星期


1-7 或者 SUN-SAT


, - * ? / L C #


年(可选)


留空, 1970-2099


, - * /

Cron 的小小说明


表示方式


意义


"0 0 12 * * ?"


Fire at 12pm (noon) every day


"0 15 10 ? * *"


Fire at 10:15am every day


"0 15 10 * * ?"


Fire at 10:15am every day


"0 15 10 * * ? *"


Fire at 10:15am every day


"0 15 10 * * ? 2005"


Fire at 10:15am every day during the year 2005


"0 * 14 * * ?"


Fire every minute starting at 2pm and ending at 2:59pm, every day


"0 0/5 14 * * ?"


Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day


"0 0/5 14,18 * * ?"


Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day


"0 0-5 14 * * ?"


Fire every minute starting at 2pm and ending at 2:05pm, every day


"0 10,44 14 ? 3 WED"


Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.


"0 15 10 ? * MON-FRI"


Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday


"0 15 10 15 * ?"


Fire at 10:15am on the 15th day of every month


"0 15 10 L * ?"


Fire at 10:15am on the last day of every month


"0 15 10 ? * 6L"


Fire at 10:15am on the last Friday of every month


"0 15 10 ? * 6L"


Fire at 10:15am on the last Friday of every month


"0 15 10 ? * 6L 2002-2005"


Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005


"0 15 10 ? * 6#3"


Fire at 10:15am on the third Friday of every month

Quartz学习笔记

时间: 2024-10-15 11:20:42

Quartz学习笔记的相关文章

Quartz学习总结

参考链接: 深入解读Quartz的原理:http://lavasoft.blog.51cto.com/62575/181907/ Java任务调度框架Quartz教程实例:http://blog.csdn.net/yuebinghaoyuan/article/details/9045471 最新quartz框架介绍:http://wenku.baidu.com/link?url=gSz5B1yZiD53xIsINzzhItRLu3SdoUnyBtITzmjswDS2p6IhUv8ps-AYak7

Quartz.NET 2.0 学习笔记(1) :Quartz.NET简介

转自:http://www.cnblogs.com/lzrabbit/archive/2012/04/13/2447609.html Quartz.NET 项目地址 http://quartznet.sourceforge.net/ Quartz.NET 2.0 学习笔记(1) :Quartz.NET简介 Quartz.NET 2.0 学习笔记(2) :和1.0的几点不同 Quartz.NET 2.0 学习笔记(3) :通过配置文件实现任务调度 Quartz.NET 2.0 学习笔记(4) :c

绘图与动画学习笔记(一)

1. 处理图形与动画的框架有 UIKit 高层次的框架,允许开发人员创建视图.窗口.按钮和其他UI相关的组件.它还将一些低级别的API引入到易于使用的高级别API中 Quartz 2D iOS上绘图的主要引擎:UIKit就使用Quartz. Core Graphics 它支持图形上下文.加载图像.绘制图像,等等. Core Animation 顾名思义,是一个帮助开发者在IOS上实现动画的框架 2. UIColor的set方法可设置Graphical context的颜色 - (void)dra

Perl语言学习笔记 day 1

1.Perl Practical Extraction and Report Language:实用摘录与报表语言 2.多行注释 (1) =需要顶格写 = description statement; =cut (2)末尾的description需要顶格写 <<description; print "Hello,world!\n"; description 3.数字: 所有数字在Perl内都以双精度浮点数格式存储; 5**3:5*5*5: 4.字符串: 最短的字符串为空字符

绘图与动画学习笔记(四)

ContextRef 详解 Graphics Context是图形上下文,可以将其理解为一块画布,我们可以在上面进行绘画操作,绘制完成后,将画布放到我们的view中显示即可,view看作是一个画框. Quartz提供了以下几种类型的Graphics Context: Bitmap Graphics Context PDF Graphics Context Window Graphics Context Layer Context Post Graphics Context 先熟悉下CGConte

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu

jQuery学习笔记(一):入门

jQuery学习笔记(一):入门 一.JQuery是什么 JQuery是什么?始终是萦绕在我心中的一个问题: 借鉴网上同学们的总结,可以从以下几个方面观察. 不使用JQuery时获取DOM文本的操作如下: 1 document.getElementById('info').value = 'Hello World!'; 使用JQuery时获取DOM文本操作如下: 1 $('#info').val('Hello World!'); 嗯,可以看出,使用JQuery的优势之一是可以使代码更加简练,使开

[原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------