准备

介绍

定时任务,无论是互联网公司还是传统的软件行业都是必不可少的。Quartz,它是好多优秀的定时任务开源框架的基础,使用它,我们可以使用最简单基础的配置来轻松的使用定时任务。

Quartz 是 OpenSymphony 开源组织在 Job scheduling 领域的又一个开源项目,它可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。

Quartz 是开源且具有丰富特性的“任务调度库”,能够集成与任何的 java 应用,小到独立的应用,大至电子商业系统。Quartz 能够创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务。任务 job 被定义为标准的 java 组件,能够执行任何你想要实现的功能。Quartz 调度框架包含许多企业级的特性,如 JTA 事务、集群的支持。

简而言之,Quartz 就是基于 java 实现的任务调度框架,用于执行你想要执行的任何任务。

Quart 官网 : http://www.quartz-scheduler.org

运行环境

  • Quartz 可以运行嵌入在另一个独立式应用程序。
  • Quartz 可以在应用程序服务器(或 Servlet 容器)内被实例化,并且参与事务。
  • Quartz 可以作为一个独立的程序运行(其自己的 Java 虚拟机内),可以通过 RMI 调用。
  • Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行。

设计模式

  • Builder 模式
  • Factory 模式
  • 组件模式
  • 链式编程

核心元素

  • Job(任务)

    Job 就是你想要实现的任务类,每一个 Job 必须实现 org.quartz.job 接口,且只需实现接口 定义的 execute 方法。

  • Trigger(触发器)

    Trigger 是为你执行任务的触发器,比如你想每天定时 3 点发送一封统计邮件,Trigger 将会设置 3 点执行该任务。

    Trigger 主要包含两种 SimpleTrigger 和 CronTrigger 两种。

  • Scheduler(调度器)

    Scheduler 是任务的调度器,它会将任务 Job 及触发器 Trigger 整合起来,负责基于 Trigger 设定的时间来执行 Job。

体系结构

常用API

以下是 Quartz 编程 API 的几个重要接口,也是 Quartz 的重要组件。

  • Scheduler:用于与调度程序交互的主程序接口。它也是调度程序-任务执行计划表,只有安排进执行计划的任务 Job (通过 scheduler,scheduleJob 方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发 Trigger),该任务才会执行。
  • Job:我们预先定义的希望在未来时间能被调度程序执行的任务类,我们可以自定义。
  • JobDetail:使用 JobDetail 来定义定时任务的实例,JobDetail 实例是通过 JobBuilder 类创建的。
  • JobDataMap:可以包含不限量(序列化的)数据对象,在 Job 实例执行的时候,可以使用其中的数据;JobDataMap 是 Java Map 接口的一个实现,额外增加了一些便于存取基本类型数据的方法。
  • Trigger:触发器,Trigger 对象是用来触发执行 Job 的。当调度一个 Job 时,我们实例一个触发器然后调整它的属性来满足 Job 执行的条件。表名任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每 2 秒就执行一次。
  • JobBuilder:用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
  • TriggerBuilder:触发器创建器,用于创建触发器 Trigger 实例。
  • JobListener、TriggerListener、SchedulerListener 监听器,用于对组件的监听。

使用

入门案例

1、创建 maven java 项目,引入如下依赖:

<?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.zze.quertz</groupId>
    <artifactId>quartz_test1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--Quartz 依赖-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!--整合 log4j-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
        <!--log4j 依赖-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <target>1.8</target>
                    <source>1.8</source>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

pom.xml

2、创建任务类:

package com.zze.quartz.job;

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

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

/**
 * 输出当前时间任务类
 */
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        Date date = new Date();
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob

3、编写执行类:

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1、调度器(Scheduler),从工厂中获取调度器实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 2、任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class) // 加载任务类,与 HelloJob 完成绑定,需实现 org.quartz.Job 接口
                .withIdentity("job1", "group1") // param1 : 任务的名称(唯一标示),必须指定 param2 : 任务组名称,未指定时默认为 DEFAULT
                .build();
        // 3、触发器(Trigger)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") // param1 : 触发器名称(唯一标示) param2 : 触发器组名称
                .startNow() // 马上启动触发器
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5)) // 每 5 秒重复执行
                .build();
        // 4、让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start(); // 启动
//        scheduler.shutdown(); // 停止
    }
}

Job和JobDetail介绍

  • Job:工作任务调度的接口,任务类需要实现该接口。该接口中定义 execute 方法,类似 JDK 提供的 TimeTask 类的 run 方法,在里面编写任务执行的业务逻辑。
  • Job 实例在 Quartz 中的生命周期:每次调度器执行 Job 时,它在调用 execute 方法前会创建一个新的 Job 实例,当调用完成后,关联的 Job 对象实例会被释放,释放的实例会被垃圾回收机制回收。
  • JobDetail:JobDetail 为 Job 实例提供了许多设置属性,以及 JobDataMap 成员变量属性,它用来存储特定 Job 实例的状态信息,调度器需要借助 JobDetail 对象来添加 Job 实例。
  • JobDetail 的重要属性:name、group、jobClass、jobDataMap。
    String jobName = jobDetail.getKey().getName();// 任务名称
    String jobGroupName = jobDetail.getKey().getGroup();// 组名称
    String jobClassName = jobDetail.getJobClass().getName();// 任务类
    System.out.println(jobName); // job1
    System.out.println(jobGroupName); // group1
    System.out.println(jobClassName); // com.zze.quartz.job.HelloJob

JobExecutingContext介绍

  • 当 Scheduler 调用一个 Job,就会将 JobExecutionContext 传递给 Job 的 execute() 方法。
  • Job 能通过 JobExecutionContext 对象访问到 Quartz 运行时的环境以及 Job 本身的明细数据。
    // 获取 JobDetail 内容
    JobDetail jobDetail = jobExecutionContext.getJobDetail();
    // 获取 Trigger 内容
    Trigger trigger = jobExecutionContext.getTrigger();
    // 获取 Scheduler 内容
    Scheduler scheduler = jobExecutionContext.getScheduler();
    // 获取当前 job 实例
    Job jobInstance = jobExecutionContext.getJobInstance();
    System.out.println(this == jobInstance); // true
    // 获取下次执行时间
    Date nextFireTime = jobExecutionContext.getNextFireTime();
    // 获取上次执行时间
    Date previousFireTime = jobExecutionContext.getPreviousFireTime();
    // 获取当前执行时间
    Date fireTime = jobExecutionContext.getFireTime();
    // 获取单次任务执行的唯一标示
    String fireInstanceId = jobExecutionContext.getFireInstanceId();
    // 获取 JobDataMap
    JobDataMap mergedJobDataMap = jobExecutionContext.getMergedJobDataMap();

JobDataMap

介绍

  • 在进行任务调度时,JobDataMap 存储在 JobExecutionContext 中,非常方便获取。
  • JobDataMap 可以用来装载任何可序列化的数据对象,当 Job 实例对象被执行时这些参数对象会传递给它。
  • JobDataMap 实现了 JDK 的 Map 接口,并且添加了非常方便的方法用来存取基础数据类型。

数据传递

可通过 JobDetail 和 Trigger 传递数据给 Job 类,如下:

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .usingJobData("msg","hello job from jobDetail") // 传递参数到任务类
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .usingJobData("msg","hello job from trigger") // 传递参数到任务类
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo :传递方

Job 类接收数据有如下两种方式:

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

/**
 * 输出当前时间任务类
 */
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);

        // 通过 JobDetail 获取 JobDataMap
        JobDataMap jobDetailJobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
        String msgFromJobDetail = jobDetailJobDataMap.getString("msg");
        System.out.println(msgFromJobDetail); // hello job from jobDetail
        // 通过 Trigger 获取 JobDataMap
        JobDataMap triggerJobDataMap = jobExecutionContext.getTrigger().getJobDataMap();
        String msgFromTrigger = triggerJobDataMap.getString("msg");
        System.out.println(msgFromTrigger); // hello job from trigger
    }
}

com.zze.quartz.job.HelloJob :方式一,通过 JobExecutionContext 接收传递过来的数据

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

/**
 * 输出当前时间任务类
 */
public class HelloJob implements Job {

    private String msg;

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);

        System.out.println(msg); // hello job from trigger
    }
}

com.zze.quartz.job.HelloJob :方式二,通过在 Job 类中提供 setter 来接收传递过来的数据

注意:通过方式二接收数据时,JobDetail 和 Trigger 传递的 key 值不可相同,否则 Trigger 传递的数据会覆盖 JobDetail 传递的数据。

有状态和无状态的Job

有状态的 Job 可以理解为多次 Job 调用期间可以持有一些状态信息,这些状态信息存储在 JobDataMap 中,而默认的无状态 Job 每次调用都会创建一个新 JobDataMap。

无状态Job

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .usingJobData("count",0) // 传递参数到任务类
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .usingJobData("msg","hello job from trigger") // 传递参数到任务类
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    private Integer count;

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
        System.out.println(count);
        count++;
        jobExecutionContext.getJobDetail().getJobDataMap().put("count", count);
    }
}

com.zze.quartz.job.HelloJob

有状态Job

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .usingJobData("count",0) // 传递参数到任务类
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .usingJobData("msg","hello job from trigger") // 传递参数到任务类
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;
@PersistJobDataAfterExecution   // 给 Job 添加上该注解那么该 Job 就变成了有状态的Job,多次调用 Job 会保存该 Job 的状态
public class HelloJob implements Job {

    private Integer count;

    public void setCount(Integer count) {
        this.count = count;
    }

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
        System.out.println(count);
        count++;
        jobExecutionContext.getJobDetail().getJobDataMap().put("count", count);
    }
}

com.zze.quartz.job.HelloJob

HelloJob 类没有添加 @PersistJobDataAfterExecution 注解时,每次调用都会新创建一个 JobDataMap,count 不会累加。

HelloJob 类添加 @PersistJobDataAfterExecution 注解时,多次 Job 调用期间会持久化 JobDataMap 信息,所以可以实现 count 的累加。

Trigger

介绍

Quartz 有一些不同的触发器类型,不过,用得最多的是 SimpleTrigger 和 CronTrigger。

  • jobKey:标识 Job 实例的标识,触发器被触发时,该 Job 实例会被执行。
  • startTime:标识触发器的时间表,第一次开始被出发的时间。
  • endTime:指定触发器终止被触发的时间。

示例

指定任务开始与结束的时间。

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

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

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException, ParseException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .usingJobData("count",0) // 传递参数到任务类
                .build();
        Date startTime = new Date();
        Date endTime = new Date();
        startTime.setTime(startTime.getTime()+3000); // 推迟 3 秒执行
        endTime.setTime(startTime.getTime()+10000); // 推迟 10 秒结束执行
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                // .startNow()
                .startAt(startTime) // 设置开始的时间
                .endAt(endTime) // 设置结束的时间
                .usingJobData("msg","hello job from trigger") // 传递参数到任务类
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo

package com.zze.quartz.job;

import org.quartz.*;

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

@PersistJobDataAfterExecution   // 给 Job 添加上该注解那么该 Job 就变成了有状态的Job,多次调用 Job 会保存该 Job 的状态
public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);

        Trigger trigger = jobExecutionContext.getTrigger();
        JobKey jobKey = trigger.getJobKey();
        Date startTime = trigger.getStartTime();
        Date endTime = trigger.getEndTime();
        System.out.printf("开始时间:%s\n", simpleDateFormat.format(startTime));
        System.out.printf("结束时间:%s\n", simpleDateFormat.format(endTime));
    }
}

com.zze.quartz.job.HelloJob

SimpleTrigger触发器

介绍

在 Quartz 中 SImpleTrigger 对于设置和使用是最为简单的一种 Trigger。

它是为那种需要在特定的日期/时间启动,且能以一个可能的间隔时间重复执行 n 次的 Job 所设计的。

示例

让一个任务在指定时间开始执行,并且只重复执行三次。

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.text.ParseException;
import java.util.Date;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException, ParseException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .build();
        Date startTime = new Date();
        startTime.setTime(startTime.getTime() + 3000); // 推迟 3 秒执行
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startAt(startTime) // 设置开始的时间
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(5).withRepeatCount(3 - 1))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob

  • SimpleTrigger 的属性有:开始时间、结束时间、重复次数和重复的时间间隔。
  • 重复次数属性的值可以为 0 、正整数、或常量 SimpleTrigger.REPEAT_IDENFINITELY (-1)。
  • 重复的时间间隔属性值必须为长整型的正整数,以毫秒为时间单位。
  • 如果有指定结束时间属性值,则结束时间属性优先于重复次数属性。

CronTrigger触发器

介绍

如果你需要像日历那样按日程来出发任务,而不是像 SimpleTrigger 那样每隔特定的间隔时间触发,CronTrigger 通常比 SimpleTrigger 更有用,因为它是基于日历的作业调度器。

使用 CronTrigger,你可以指定例如“每周五中午 12:00”,或者“每隔工作日的 9:30”或“从每个周一、周三、周五的上午 9:00 到 上午 10:00 之间每间隔 5 分钟”这样的日程安排来触发。甚至,像 SimpleTrigger 一样,CronTrigger 也有一个 startTime 以指定日程从什么时候开始,也有一个 endTime(可选)来指定日程何时终止不再继续。

Cron表达式

Cron 表达式用来配置 CronTrigger 实例。Cron 表达式是一个由 7 个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:

Seconds Minutes Hours DayofMonth Month DayofWeek Year(可选)
各字段含义
字段 允许值 允许的特殊字符
秒(Seconds) 0~59的整数 , - * /    四个字符
分(Minutes) 0~59的整数 , - * /    四个字符
小时(Hours 0~23的整数 , - * /    四个字符
日期(DayofMonth 1~31的整数(但是你需要考虑你月的天数) ,- * ? / L W C     八个字符
月份(Month 1~12的整数或者 JAN-DEC , - * /    四个字符
星期(DayofWeek 1~7的整数或者 SUN-SAT (1=SUN) , - * ? / L C #     八个字符
年(可选,留空)(Year 1970~2099 , - * /    四个字符

每一个域都可以使用数字,但还可以出现如下特殊字符,它们的含义如下:

*:表示匹配该域的任意值。假如在 Minutes 域使用 * , 即表示每分钟都会触发事件。
?:只能用在 DayofMonth 和 DayofWeek 两个域。它也匹配域的任意值,但实际不会。因为 DayofMonth 和 DayofWeek 会相互影响。例如想在每月的 20 日触发调度,不管 20 日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用 ? ,而不能使用 * ,如果使用 * 表示不管星期几都会触发,实际上并不是这样。
-:表示范围。例如在 Minutes 域使用5-20,表示从5分到20分钟每分钟触发一次
/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在 Minutes 域使用 5/20,则意味着 5 分钟触发一次,而 25,45 等分别触发一次.
,:表示列出枚举值。例如:在 Minutes 域使用 5,20,则意味着在 5 和 20 分分别触发一次。
L:表示最后,只能出现在 DayofWeek 和 DayofMonth 域。如果在 DayofWeek 域使用 5L,意味着在最后的一个星期四触发。
W:表示有效工作日(周一到周五),只能出现在 DayofMonth 域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth 使用 5W,如果 5 日是星期六,则将在最近的工作日:星期五,即 4 日触发。如果 5 日是星期天,则在 6 日(周一)触发;如果 5 日在星期一到星期五中的一天,则就在 5 日触发。另外一点,W 的最近寻找不会跨过月份 。
LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
#:用于确定每个月第几个星期几,只能出现在 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
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 触发

在线Cron表达式生成器

示例

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.text.ParseException;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException, ParseException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .build();

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                // 使用 Cron 调度器
                .withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * 6 3 ?")) // 3 月 6 号 每隔 3 秒触发
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob

Scheduler

相关API

scheduler.start(); // 调度器启动
scheduler.standby(); // 挂起
scheduler.shutdown(false); // 立即关闭调度器
scheduler.shutdown(true); // 等待所有任务执行完毕后再终止调度器

配置

通过之前的学习已经知道,调度器实例是通过 StdSchedulerFactory.getDefaultScheduler() 创建。查看该部分源码会发现,创建调度器时加载了如下属性文件:

# 用来区分特定的调度器实例,可以按照功能用途给调度器起名
org.quartz.scheduler.instanceName:DefaultQuartzScheduler
# 这个属性与前者一样,允许任意字符串,但这个值必须在所有调度器实例中是唯一的,尤其是再一个集群环境中,作为集群的唯一 key。设为 auto 即让 Quartz 自动生成这个值
org.quartz.scheduler.instanceId:auto

org.quartz.scheduler.rmi.export:false

org.quartz.scheduler.rmi.proxy:false

org.quartz.scheduler.wrapJobExecutionInUserTransaction:false
#Quartz 自带的线程池实现类,实现了 org.quartz.threadPool
org.quartz.threadPool.class:org.quartz.simpl.SimpleThreadPool

# 处理 Job 的线程个数,至少为 1 ,最多最好不要超过 100。
org.quartz.threadPool.threadCount:10
# 线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为 1 ,最大为 10,默认 为 5
org.quartz.threadPool.threadPriority:5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread:true

org.quartz.jobStore.misfireThreshold:60000

org.quartz.jobStore.class:org.quartz.simpl.RAMJobStore

quartz-2.3.0.jar!/org/quartz/quartz.properties

我们可以在 classpath 下提供一个和上述同名的一个属性文件来覆盖 Quartz 的默认配置,也可以通过如下方式手动加载配置文件来初始化调度器:

StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
// stdSchedulerFactory.initialize(String fileName);
// stdSchedulerFactory.initialize(Properties properties);
// stdSchedulerFactory.initialize(InputStream propertiesStream);
Scheduler scheduler = stdSchedulerFactory.getScheduler();

Listener

介绍

Quartz 的监听器用于在任务调度中你我们所关注的事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信的提醒。Quartz 监听器主要有 JobListener、TriggerListener、SchedulerListener 三种,顾名思义,分别表示任务、触发器、调度器对应的监听器,三者的使用方法类似。

而这几种监听器又分为两类,全局监听器和非全局监听器,二者的区别在于:全局监听器能够接收到所有的 Job/Trigger 的时间通知;而非全局监听器只能接收到在其上注册的 Job 或 Trigger 事件,不在其上注册的 Job 或 Trigger 则不会监听。

JobListener

任务调度过程中,与任务 Job 相关的事件包括:Job 要开始执行时、Job 执行完成时。要定义一个任务监听器,需要实现 JobListener 接口:

package org.quartz;

public interface JobListener {
    // 获取 JobListener 的名称
    String getName();
    // Scheduler 在 JobDetail 将要被执行时调用
    void jobToBeExecuted(JobExecutionContext var1);
    // Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决时调用
    void jobExecutionVetoed(JobExecutionContext var1);
    // Scheduler 在 JobDetail 被执行之后调用这个方法
    void jobWasExecuted(JobExecutionContext var1, JobExecutionException var2);
}

org.quartz.JobListener

使用示例:

package com.zze.quartz.listener;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

public class HelloJobListener implements JobListener {
    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
        String jobName = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.printf("jobToBeExecuted---Job的名称为:%s,Scheduler 在 Job 将要被执行时调用该方法\n", jobName);
    }

    @Override
    public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
        String jobName = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.printf("jobExecutionVetoed---Job的名称为:%s,Scheduler 在 Job 即将被执行,但又被 TriggerListener 否决时调用该方法\n", jobName);
    }

    @Override
    public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
        String jobName = jobExecutionContext.getJobDetail().getKey().getName();
        System.out.printf("jobExecutionVetoed---Job的名称为:%s,Scheduler 在 Job 执行完成之后调用该方法\n\n", jobName);
    }
}

com.zze.quartz.listener.HelloJobListener:任务监听器

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob:任务类

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import com.zze.quartz.listener.HelloJobListener;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.KeyMatcher;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        // 注册全局 Job 监听器
//        scheduler.getListenerManager().addJobListener(new HelloJobListener(), EverythingMatcher.allJobs());
        // 注册局部 Job 监听器,对指定组、指定名称的 Job 进行监听
        scheduler.getListenerManager().addJobListener(new HelloJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo:注册监听器

TriggerListener

任务调度过程中,与触发器 Trigger 相关的事件包括:触发器触发时、触发器未正常触发时、触发器完成时等。要定义一个触发器监听器,需要实现 TriggerListener 接口:

package org.quartz;

import org.quartz.Trigger.CompletedExecutionInstruction;

public interface TriggerListener {
    // 获取 Trigger 名称
    String getName();
    // 当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将被执行时,Scheduler 就调用该方法。
    void triggerFired(Trigger var1, JobExecutionContext var2);
    // 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用该方法。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。
    boolean vetoJobExecution(Trigger var1, JobExecutionContext var2);
    // Scheduler 调用这个方法是在 Trigger 错过触发时。
    void triggerMisfired(Trigger var1);
    // Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。
    void triggerComplete(Trigger var1, JobExecutionContext var2, CompletedExecutionInstruction var3);
}

org.quartz.TriggerListener

使用示例:

package com.zze.quartz.listener;

import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.TriggerListener;

public class HelloTriggerListener implements TriggerListener {
    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) {
        String triggerName = trigger.getKey().getName();
        System.out.printf("triggerFired---%s 将要被触发\n", triggerName);
    }

    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) {
        String triggerName = trigger.getKey().getName();
        boolean isTrigger = false;
        String msg = !isTrigger ? "允许被触发" : "拒绝被触发";
        System.out.printf("vetoJobExecution---%s %s\n", triggerName, msg);
        return isTrigger; // 如果返回 true,将不会执行 Job 的方法。
    }

    @Override
    public void triggerMisfired(Trigger trigger) {
        String triggerName = trigger.getKey().getName();
        System.out.printf("triggerMisfired---%s 错过触发\n", triggerName);
    }

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, Trigger.CompletedExecutionInstruction completedExecutionInstruction) {
        String triggerName = trigger.getKey().getName();
        System.out.printf("triggerComplete---%s 触发完成\n\n", triggerName);
    }
}

com.zze.quartz.listener.HelloTriggerListener:触发器监听器

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob:任务类

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import com.zze.quartz.listener.HelloTriggerListener;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.EverythingMatcher;
import org.quartz.impl.matchers.KeyMatcher;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        // 注册一个全局的 Trigger 监听器
        // scheduler.getListenerManager().addTriggerListener(new HelloTriggerListener(), EverythingMatcher.allTriggers());
        // 注册一个局部的 Trigger 监听器
        scheduler.getListenerManager().addTriggerListener(new HelloTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1")));
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo:注册监听器

SchedulerListener

任务调度过程中,与调度器有关的事件包括:增加一个 Job/Trigger、删除一个 Job/Trigger、Scheduler 发生错误、关闭 Scheduler 等。要定义一个调度器监听器,需要实现 SchedulerListener 接口:

package org.quartz;

public interface SchedulerListener {
    // 部署 JobDetail 时调用
    void jobScheduled(Trigger trigger);
    // 卸载 JobDetail 时调用
    void jobUnscheduled(TriggerKey triggerKey);
    // 当一个 Trigger 再也不会触发时调用,若这个触发器对应的 Job 没有状态持久化,则将会从 Scheduler 中移除
    void triggerFinalized(Trigger trigger);
    // 在一个 Trigger 被暂停时
    void triggerPaused(TriggerKey triggerKey);
    // 在一个 TriggerGroup 被暂停时
    void triggersPaused(String triggerGroup);
    // 在一个 Trigger 恢复时
    void triggerResumed(TriggerKey triggerKey);
    // 在一个 TriggerGroup 恢复时
    void triggersResumed(String var1);
    // 当新加入一个 Job 时
    void jobAdded(JobDetail jobDetail);
    // 当移除一个 Job 时
    void jobDeleted(JobKey jobKey);
    // 当 Job 暂停时
    void jobPaused(JobKey jobKey);
    // 当 JobGroup 暂停时
    void jobsPaused(String jobGroup);
    // 当 Job 恢复时
    void jobResumed(JobKey jobKey);
    // 当 JobGroup 恢复时
    void jobsResumed(String jobGroup);
    // 当 Scheduler 运行期出现错误时
    void schedulerError(String msg, SchedulerException cause);
    // 当 Scheduler 挂起时
    void schedulerInStandbyMode();
    // 当 Scheduler 开始后
    void schedulerStarted();
    // 当 Scheduler 开始时
    void schedulerStarting();
    // 当 Scheduler 关闭后
    void schedulerShutdown();
    // 当 Scheduler 关闭时
    void schedulerShuttingdown();
    // 当 Scheduler 中数据被清除时
    void schedulingDataCleared();
}

org.quartz.SchedulerListener

使用示例:

package com.zze.quartz.listener;

import org.quartz.*;

public class HelloSchedulerListener implements SchedulerListener {

    @Override
    public void jobScheduled(Trigger trigger) {
        String name = trigger.getKey().getName();
        System.out.println(name + "完成部署");
    }

    @Override
    public void jobUnscheduled(TriggerKey triggerKey) {
        String name = triggerKey.getName();
        System.out.println(name + "完成卸载");
    }

    @Override
    public void triggerFinalized(Trigger trigger) {
        System.out.println(trigger.getKey().getName() + "触发器已移除");
    }

    @Override
    public void triggerPaused(TriggerKey triggerKey) {
        System.out.println(triggerKey.getName() + "触发器被暂停");
    }

    @Override
    public void triggersPaused(String triggerGroup) {
        System.out.println(triggerGroup + "触发器组被暂停");
    }

    @Override
    public void triggerResumed(TriggerKey triggerKey) {
        System.out.println(triggerKey.getName() + "触发器恢复");
    }

    @Override
    public void triggersResumed(String triggerGroup) {
        System.out.println(triggerGroup + "触发器组恢复");
    }

    @Override
    public void jobAdded(JobDetail jobDetail) {
        System.out.println(jobDetail.getKey().getName() + "任务已添加");
    }

    @Override
    public void jobDeleted(JobKey jobKey) {
        System.out.println(jobKey.getName() + "任务已删除");
    }

    @Override
    public void jobPaused(JobKey jobKey) {
        System.out.println(jobKey.getName() + "任务已暂停");
    }

    @Override
    public void jobsPaused(String jobGroup) {
        System.out.println(jobGroup + "任务组已暂停");
    }

    @Override
    public void jobResumed(JobKey jobKey) {
        System.out.println(jobKey.getName() + "任务已恢复");
    }

    @Override
    public void jobsResumed(String jobGroup) {
        System.out.println(jobGroup + "任务组已恢复");
    }

    @Override
    public void schedulerError(String msg, SchedulerException cause) {
        System.out.println("调度器发生错误:"+msg);
    }

    @Override
    public void schedulerInStandbyMode() {
        System.out.println("调度器挂起");
    }

    @Override
    public void schedulerStarted() {
        System.out.println("调度器已开始");
    }

    @Override
    public void schedulerStarting() {
        System.out.println("调度器正在开始");
    }

    @Override
    public void schedulerShutdown() {
        System.out.println("调度器关闭");
    }

    @Override
    public void schedulerShuttingdown() {
        System.out.println("调度器关闭中");
    }

    @Override
    public void schedulingDataCleared() {
        System.out.println("调度器数据被清理");
    }
}

com.zze.quartz.listener.HelloSchedulerListener:调度器监听器

package com.zze.quartz.job;

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

import java.text.SimpleDateFormat;

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(jobExecutionContext.getFireTime());
        System.out.println("正在执行 xxx 任务,时间为" + dateStr);
    }
}

com.zze.quartz.job.HelloJob:任务类

package com.zze.quartz.main;

import com.zze.quartz.job.HelloJob;
import com.zze.quartz.listener.HelloSchedulerListener;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class HelloSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(3))
                .build();
        scheduler.scheduleJob(jobDetail, trigger);
        // 注册调度器监听
        scheduler.getListenerManager().addSchedulerListener(new HelloSchedulerListener());
        scheduler.start();
    }
}

com.zze.quartz.main.HelloSchedulerDemo:注册监听器

Spring集成Quartz

依赖

创建 maven web 工程,引入以下依赖:

<?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.zze.quartz</groupId>
    <artifactId>quartz_spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>8080</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

pom.xml

加载 Spring 核心配置文件:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

WEB-INF/web.xml

XML方式

1、创建任务类:

package com.zze.quartz;

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

public class HelloJob {
    public void run() {
        System.out.println("Hello quartz! now ->" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

com.zze.quartz.HelloJob

2、Spring 配置:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <!-- 定时任务的bean -->
    <bean id="helloJob" class="com.zze.quartz.HelloJob"/>

    <bean id="testQuartzJob_jd"
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject">
            <ref bean="helloJob"/>
        </property>
        <property name="targetMethod" value="run"></property>
    </bean>

    <!-- 调度的配置&job的配置 -->
    <bean id="testQuartzJob_ct" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail">
            <ref bean="testQuartzJob_jd"/>
        </property>
        <!--每五秒执行-->
        <property name="cronExpression" value="*/5 * * * * ?"></property>
    </bean>

    <!-- 开启定时任务-->
    <bean id="startQuertz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="testQuartzJob_ct"/>
            </list>
        </property>
    </bean>
</beans>

applicationContext.xml

3、启动项目,观察控制台:

注解方式

1、创建任务类并使用注解:

package com.zze.quartz;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class HelloJob {
    @Scheduled(cron = "*/1 * * * * ?") // 每秒执行
    public void run() {
        System.out.println("Hello quartz! now ->" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    }
}

com.zze.quartz.HelloJob

2、配置 Spring 包扫描:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-3.0.xsd">

    <!-- task任务扫描注解 -->
    <task:annotation-driven/>
    <!-- 扫描位置 -->
    <context:component-scan base-package="com.zze.quartz"/>
</beans>

applicationContext.xml

3、启动项目,观察控制台:

java框架之Quartz-任务调度&整合Spring的更多相关文章

  1. 【Java EE 学习 81】【CXF框架】【CXF整合Spring】

    一.CXF简介 CXF是Apache公司下的项目,CXF=Celtix+Xfire:它支持soap1.1.soap1.2,而且能够和spring进行快速无缝整合. 另外jax-ws是Sun公司发布的一 ...

  2. 《SSM框架搭建》三.整合spring web

    感谢学习http://blog.csdn.net/zhshulin/article/details/37956105#,还是修改了spring到最新的版本和接口开发示例 根据前一篇日志,已经有了myb ...

  3. 搭建Spring + SpringMVC + Mybatis框架之二(整合Spring和Mybatis)

    整合Spring和Mybatis 首先给出完整的项目目录: (1)引入项目需要的jar包 使用http://maven.apache.org作为中央仓库即可. Spring核心包,mybatis核心包 ...

  4. quartz使用(整合spring)

    quartz与spring整合后,还是需要Scheduler实例.JobDetail实例.Trigger实例,只不过是用FactoryBean的方式创建了. 在spring-context-suppo ...

  5. JAVA框架--hibernate、struts2、spring

    JAVAEE——spring01:介绍.搭建.概念.配置详解.属性注入和应用到项目   JAVAEE——struts2_04:自定义拦截器.struts2标签.登陆功能和校验登陆拦截器的实现   JA ...

  6. Java框架知识点总结

    一.Struts1的运行原理 在启动时通过前端总控制器ActionServlet加载struts-config.xml并进行解析,当用户在jsp页面发送请求被struts1的核心控制器ActionSe ...

  7. cxf整合spring中出现的错误

    Caused by: java.lang.ClassNotFoundException: javax.wsdl.extensions.ElementExtensible at org.apache.c ...

  8. 从零开始学 Java - Spring 使用 Quartz 任务调度定时器

    生活的味道 睁开眼看一看窗外的阳光,伸一个懒腰,拿起放在床一旁的水白开水,甜甜的味道,晃着尾巴东张西望的猫猫,在窗台上舞蹈.你向生活微笑,生活也向你微笑. 请你不要询问我的未来,这有些可笑.你问我你是 ...

  9. Spring整合的quartz任务调度的实现方式

    一.在web.xml中将配置文件的位置指定好. Web.xml的配置如下: <?xmlversion="1.0"encoding="UTF-8"?> ...

  10. java框架之Spring(4)-Spring整合Hibernate和Struts2

    准备 导包 Struts2 导入 Struts2 zip 包解压目录下 'apps/struts-blank.war' 中所有 jar 包,如下: asm-3.3.jar asm-commons-3. ...

随机推荐

  1. XAMARIN上运行IPHONE模拟器

    重装农药第32天!!! 今天弄XAMARIN运行IPHONE模拟器,前提是需要MAC 同时在开着,然后打开昨天 建立的HELLO WORLD项目,选择APP1.IOS,直接点右边的三角运行即可,他会自 ...

  2. 使用 Node.js 搭建API 网关

    外部客户端访问微服务架构中的服务时,服务端会对认证和传输有一些常见的要求.API 网关提供共享层来处理服务协议之间的差异,并满足特定客户端(如桌面浏览器.移动设备和老系统)的要求. 微服务和消费者 微 ...

  3. 4 Git 基础 - 撤消操作

    撤消操作 任何时候,你都有可能需要撤消刚才所做的某些操作.接下来,我们会介绍一些基本的撤消操作相关的命令.请注意,有些撤销操作是不可逆的,所以请务必谨慎小心,一旦失误,就有可能丢失部分工作成果. 修改 ...

  4. Oracle sql 统计

    1.按小时统计数量 SELECT TO_CHAR(RECEIVE_TIME,'HH24') HOUR ,COUNT(*) N FROM PH_PRESCRIPTION P WHERE TO_CHAR( ...

  5. 使用Mybatis-Generator自动生成Dao、Model、Mapping

    Mybatis属于半自动ORM,在使用这个框架中,工作量最大的就是书写Mapping的映射文件,由于手动书写很容易出错,我们可以利用Mybatis-Generator来帮我们自动生成文件. 1.相关文 ...

  6. RabbitMQ内存爆出

    RabbitMQ升级到3.6.1版本后,随着业务和系统功能的增加,出现RabbitMQ内存陡增直至服务宕掉的情况.内存增加时,在management管理控制台上可以见到如下警告: The manage ...

  7. 博客搬家了,新域名dinphy.wang

    博客搬家了,新域名      dinphy.wang 博客搬家了,新域名      www.dinphy.wang 博客搬家了,新域名      dinphy.wang 博客搬家了,新域名     w ...

  8. NaviSoft31.源码开发完成

    NaviSoft是作者一人开发完成,从之前的1.0版本,到现在的3.1版本.历经2年时间,开发完成 下面是NaviSoft的源码结构 加QQ群了解更多信息

  9. 梯度下降(gradient descent)算法简介

    梯度下降法是一个最优化算法,通常也称为最速下降法.最速下降法是求解无约束优化问题最简单和最古老的方法之一,虽然现在已经不具有实用性,但是许多有效算法都是以它为基础进行改进和修正而得到的.最速下降法是用 ...

  10. JavaScript之JS的数据类型

    前言 JavaScript一共有6中数据类型: 基本数据类型(5):字符串(String).数字(Number).布尔(Boolean).数组(Array).空(Null).未定义(Undefined ...