天天看点

定时任务调度工具之Quartz(一) 转载

原创出处:癫狂编程

一、Quartz介绍

  • OpenSymphony提供的强大的开源任务调度框架;
  • 官网:http://www.quartz-scheduler.org/
  • 纯Java实现,精细控制排程;

1.特点

(1)强大的调度功能:作为spring默认的调度框架,很容易与spring集成,实现灵活可配置的调度功能;
还提供了调度运行环境的持久化机制,可以保存并恢复调度现场,
即使系统因故障关闭,任务调度现场数据并不会丢失。

(2)灵活的应用方式:允许开发者灵活的定义触发器的调度时间表并可以为触发器和任务进行关联映射。

(3)分布式和集群能力。           

2.主要用到的设计模式

Builder模式
Factory模式
组件模式
链式写法           

3.三个核心概念

调度器:负责定期定时定频率的去执行任务
任务:包括了业务逻辑
触发器:让东西生效的时间           

4.Quartz的体系结构

定时任务调度工具之Quartz(一) 转载

5.重要组成

(1)Job:

区别与JobDetail,是一个接口,只有一个方法void execute(JobExecutionContext context),
开发者可以实现该接口定义运行任务,相当于TimerTask下面的run()方法,区别在于,
Job有一个参数JobExecutionContext,JobExecutionContext这个类提供了调度上下文的各种信息,
Job运行时的信息就保存在JobExecutionContext里的JobDataMap实例中。           

(2)JobDetail:

Quartz在每次执行实例的时候都重新创建一个job实例,所以它不直接接受一个job实例,
而是通过接受一个job实现类,以便运行时通过new Instance()的反射机制实例化job,
因此需要通过一个类来描述job的实现类及其他相关静态信息,如job的名字,描述,关联监听器等信息。
(即用来绑定job,并且在job执行的时候携带一些执行的信息)
通过该类的构造函数可以更具体地了解它的功用:
JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),
该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称;           

(3)JobBuilder:

用来定义或者创建JobDetail的实例,JobDetail限定了只能是job的实例。           

(4)JobStore:

接口,用来保存job数据,实现类主要有RAMJobStore,JobStoreTX,JobStoreCMT;
JobStoreTX和JobStoreCMT均将数据保存在数据库中,
RAMJobStore将数据保存在内存中,保存一些执行的信息。           

(5)Trigger:

一个类,描述触发的job执行时的时间触发规则;主要有SimpleTrigger和CronTrigger两个子类。
当仅触发一次或者以固定时间间隔周期执行时,使用SimpleTrigger;
CronTrigger通过cron表达式,定义出各种复杂时间规则的调度方案,
如每天早晨的固定时间执行,或周二周三的固定时间执行等需求。           

(6)TriggerBuilder:

使用builder模式,用来定义或者创建触发器的实例           

(7)ThreadPool:

Timer有且只有一个后台线程在执行,Quartz的schedule下有ThreadPool整个线程池来运行,
schedule使用线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行的效率,
从而解决并发问题           

(8)Scheduler:

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

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

(9)Calendar:

一个Trigger可以和多个Calendar关联,以排除或包含某些时间点。
比如某个任务希望放假时间不执行。           

(10)监听器:

JobListener,TriggerListener,SchedulerListener;分别对Job,
Trigger,Scheduler的事件进行监听,包括scheduler一运行起来的时
候,或者在执行任务的时候,或终止的时候进行监听,监听的时候加入一些
自定义的某些逻辑,比如打印日志信息。           

二、第一个Quartz程序

需求:让任务每2秒打印一次helloworld

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hcx.demo</groupId>
    <artifactId>HelloQuartz</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

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

    </dependencies>

</project>           

任务类:

package com.hcx.HelloQuartz;

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

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

/**
 * Created by HCX on 2017/9/6.
 * 自定义任务
 */
public class HelloJob implements Job {

    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        //打印当前的执行时间,格式为:2017-09-06 00:00:00
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前的时间为(HelloJob):" + sf.format(date));
        //编写具体的业务逻辑
        System.out.println("Hello Job!");
    }

}           

任务调度类:

package com.hcx.HelloQuartz;

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;

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

/**
 * Created by HCX on 2017/9/6.
 */
public class HelloScheduler {

    public static void main(String[] args) throws SchedulerException {
        /**
         * JobDetail:用来绑定Job,并且在job执行的时候携带一些执行的信息
         */
        //创建一个JobDetail实例,将该实例与HelloJob Class绑定
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob","group1").build();

        /**
         * Trigger:用来触发job去执行的,包括定义了什么时候去执行,
         * 第一次执行,是否会一直重复地执行下去,执行几次等
         */
        //创建一个Trigger实例,定义该job立即执行,并且每隔2秒钟重复执行一次,直到程序停止
        /**
         * trigger通过builder模式来创建,TriggerBuilder.newTrigger()
         * withIdentity():定义一个标识符,定义了组
         * startNow():定义现在开始执行,
         * withSchedule(SimpleScheduleBuilder.simpleSchedule():withSchedule也是builder模式创建
         *.withIntervalInSeconds(2).repeatForever()):定义了执行频度:每2秒钟执行一次,不间断地重复执行
         * build():创建trigger
         */
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger","group1").startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(2).repeatForever()).build();

        //创建scheduler实例:
        /**
         * scheduler区别于trigger和jobDetail,是通过factory模式创建的
         */
        //创建一个ScheduleFactory
        SchedulerFactory sfact = new StdSchedulerFactory();
        Scheduler scheduler = sfact.getScheduler();
        scheduler.start();

        //打印当前时间
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前的时间为(HelloScheduler):" + sf.format(date));

        //需要将jobDetail和trigger传进去,并将jobDetail和trigger绑定在一起。
        scheduler.scheduleJob(jobDetail,trigger);

    }
}           

好的代码像粥一样,都是用时间熬出来的。——这也是原文里的2333,觉得很有道理