基于Spring+Quartz 搭建定时任务调度框架

尘心
尘心
尘心
9
文章
0
评论
2020-09-2313:09:00 评论 1,082 20759字
摘要

项目总结:(1)对于学习一个新的技术,首先要了解其概念,然后了解它是什么?能做什么?优点是什么?缺点是什么?核心是什么?(2)实战使用,加深印象,在实战中发现问题解决问题,这样提升才是很快的;(3)记录笔记,笔记方便后期遗忘查看,有利于反复记忆,是一笔经验财富。

目录:

  • Quartz 任务调度框架介绍、入门以及基本使用
  • 基本介绍
  • Quartz能做什么?
  • 为什么要用Quartz?
  • Quartz入门精讲
  • 使用 Spring + Maven 构建项目
  • Spring 基本介绍
  • Maven基本介绍
  • 基于idea使用Spring + Maven构建项目
  • 集成 Quartz 任务调度框架
  • 依赖集成Quartz
  • 排除冲突
  • 实战应用开发
  • 编写作业
  • 创建配置文件读取类
  • 编写Quartz任务管理器
  • 编写启动类
  • cron表达式详细介绍
  • 测试验收项目以及项目总结。将项目打包成轻量级依赖包,供其它项目使用
  • 启动测试
  • 打包
  • 项目总结

Quartz 任务调度框架介绍、入门以及基本使用

基本介绍:

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是完全由java开发的一个开源的任务日程管理系统,"任务进度管理器"就是一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统。

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

基于Spring+Quartz 搭建定时任务调度框架

简单总结:

  • Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。
  • Quartz 可以与 J2EE 与 J2SE 应用程序相结合也可以单独使用。
  • Quartz 允许程序开发人员根据时间的间隔来调度作业。
  • Quartz 实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。

Quartz能做什么?

  • 作业调度:调用各种框架的作业脚本,例如shell,hive等
  • 定时任务:在某一预定的时刻,执行你想要执行的任务

为什么要用Quartz?

  • 经过市场验证,开源,免费
  • 动态调度任务,api丰富,满足多种业务场景
  • 支持多线程,分布式,集群
  • 支持持久化存储,运行在内存中,可调用JDBC进行持久化存储
  • 支持故障转移,负载均衡

Quartz入门精讲

1. 下载安装

方式一:官网下载 http://www.quartz-scheduler.org/downloads/ 下载之后可以看到这样一个目录,打开将lib中的quartz-2.*-xx.jar添加到项目中

就可以使用啦

方式二:使用maven

        <!-- 引入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.2.1</version>        </dependency>

坑点:确保应用中只有一个版本的Quartz,否则多版本容易导致引用不正确的类,从而导致项目无法正常运行。需要手动取排除掉这些jar包!

2.quartz.properties配置文件

a. 该配置文件不是必须的,如果使用最基本的配置,该文件必须位于classpath下。基本配置如下:

org.quartz.scheduler.instanceName = MySchedulerorg.quartz.threadPool.threadCount = 3org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

配置解释:调度程序名称为MyScheduler,线程池最多可同时运行3个作业,作业的所有信息都存储到内存中

b. 如果你准备构建一个使用quartz的web应用(以.war包的形式),你应该将quartz.properties文件放到WEB-INF/classes目录下。

c. 详细配置手册,有需求是可查看手册: https://www.w3cschool.cn/quartz_doc/quartz_doc-i7oc2d9l.html

3.第一个程序

来看看官方给的示例:

import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.impl.StdSchedulerFactory;import static org.quartz.JobBuilder.*;import static org.quartz.TriggerBuilder.*;import static org.quartz.SimpleScheduleBuilder.*;public class QuartzTest {  public static void main(String[] args) {      try {          // Grab the Scheduler instance from the Factory          Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();        // define the job and tie it to our HelloJob class        JobDetail job = new Job(HelloJob.class)          .withIdentity("job1", "group1")          .build();                // Trigger the job to run now, and then repeat every 40 seconds        Trigger trigger = new Trigger()          .withIdentity("trigger1", "group1")          .startNow()                .withSchedule(simpleSchedule()                  .withIntervalInSeconds(40)                  .repeatForever())                      .build();                // Tell quartz to schedule the job using our trigger        scheduler.scheduleJob(job, trigger);          // and start it off        scheduler.start();        scheduler.shutdown();      } catch (SchedulerException se) {          se.printStackTrace();      }  }}

解读:这是一个很精简的例子,包含了一个基本的使用过程

  • 首先创建一个Scheduler示例
  • 然后定义一个作业job, 作业中有需要执行的内容
  • 创建一个触发器,其中包含触发方式和条件
  • 将作业,和定时任务传入scheduler中
  • 启动
  • 关闭
4.Quartz核心概念
  • Scheduler: 与调度程序交互的主要API。
  • Job: 由希望由调度程序执行的组件实现的接口。
  • JobDetail: 用于定义作业的实例。
  • Trigger(即触发器): 定义执行给定作业的计划的组件。
  • JobBuilder: 用于定义/构建JobDetail实例,用于定义作业的实例。
  • TriggerBuilder: 用于定义/构建触发器实例。
5.Job编写
  public class HelloJob implements Job {    public HelloJob() {    }    public void execute(JobExecutionContext context)      throws JobExecutionException {            System.err.println("Hello!  HelloJob is executing.");    }  }

很简单, 我们只需要实现Job接口, 然后实现execute方法即可。在execute中写我们的业务逻辑

即可

那么, 假如我需要在execute中接收参数,进行业务处理怎么办呢? 请看下一点!

6.JobDataMap传递参数
    /**     * 创建一个定时作业任务并启动     *     * @param jobName     * @param cls     * @param cron     * @param command     */    public static void addJob(String jobName, Class cls, String cron, String command) {        try {            Scheduler sched = gSchedulerFactory.getScheduler();            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();            JobDataMap jobDataMap = job.getJobDataMap();            // 传递参数            jobDataMap.put("command", command);//            jobDataMap.put("timingExpId", timingExpId);            // 表达式调度构建器            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);            // 获取当前系统时间 转成 北京时间            Date currTime = new Date(System.currentTimeMillis());            Date date = changeTimeZone(currTime, TimeZone.getDefault(), TimeZone.getTimeZone("Asia/Shanghai"));            // 按新的cronExpression表达式构建一个新的trigger            Trigger trigger = TriggerBuilder.newTrigger()                    .withIdentity(jobName, TRIGGER_GROUP_NAME)                    .withSchedule(scheduleBuilder)                    .startAt(date)// 设置开始计时时间                    .build();            // 交给scheduler去调度            sched.scheduleJob(job, trigger);            // 启动            if (!sched.isShutdown()) {                sched.start();                logger.info("新任务创建完毕,并启动 !jobName:[" + jobName + "]");            }        } catch (Exception e) {            logger.error("新任务创建、并启动 失败", e);        }    }
  • 实例化job对象后,获取JobDataMap对象 JobDataMap jobDataMap = job.getJobDataMap();
  • 在jobDataMap中设置需要传递的参数jobDataMap.put("command", command);
  • 在Job的execute中接收参数
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {        JobDataMap dataMap = jobExecutionContext.getMergedJobDataMap();        String command = (String) dataMap.get("command");   // 接收JobDetail 传递过来的参数    }
  • 用户只能创建一个Job类,然后创建多个与该job关联的JobDetail实例,每一个实例都有自己的属性集和JobDataMap,最后,将所有的实例都加到scheduler中。
7. Trigger的公共属性

所有类型的trigger都有TriggerKey这个属性,表示trigger的身份;除此之外,trigger还有很多其它的公共属性。这些属性,在构建trigger的时候可以通过TriggerBuilder设置。

trigger的公共属性有:

  • jobKey属性:当trigger触发时被执行的job的身份;
  • startTime属性:设置trigger第一次触发的时间;该属性的值是java.util.Date类型,表示某个指定的时间点;有些类型的trigger,会在设置的startTime时立即触发。

优先级(priority)

  • 设置优先级,正数负数均可

注意:只有同时触发的trigger之间才会比较优先级。10:59触发的trigger总是在11:00触发的trigger之前执行。

注意:如果trigger是可恢复的,在恢复后再调度时,优先级与原trigger是一样的。

错过触发(misfire Instructions)

  • trigger的misfire属性;如果scheduler关闭了,或者Quartz线程池中没有可用的线程来执行job,此时持久性的trigger就会错过(miss)其触发时间,即错过触发(misfire)。

日历示例(calendar)

  • Quartz的Calendar对象(不是java.util.Calendar对象)可以在定义和存储trigger的时候与trigger进行关联。Calendar用于从trigger的调度计划中排除时间段。
8. 注解控制Job状态和并发

这些注解通常加载Job类上:

  • @DisallowConcurrentExecution 通知Quartz不要并发地执行同一个job类的多个实例。
  • @PersistJobDataAfterExecution 成功执行了job类的execute方法后,没有出现异常,则更新JobDetail中JobDataMap的数据
  • @DisallowConcurrentExecution 控制Job实例并发执行,使其有序一个接一个执行
9. Job其它属性

通过JobDetail对象,可以给job实例配置的其它属性:

  • Durability 非持久化,不活跃的trigger与之关联的时候,会被自动地从scheduler中删除
  • RequestsRecovery scheduler发生硬关闭(hard shutdown) 例如关机,则当scheduler重新启动的时候,该job会被重新执行。

使用 Spring + Maven 构建项目

1. Spring 基本介绍:

Spring这东西相信大家都很熟悉了,但是为了照顾到所有同学,还是在这里简的概述一下。有基础的同学可以直接跳过

基于Spring+Quartz 搭建定时任务调度框架

  • Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
  • Spring 框架是一个开源的 Java 平台,它最初是由 Rod Johnson 编写的,并且于 2003 年 6 月首次在 Apache 2.0 许可下发布。
  • Spring 是轻量级的框架,其基础版本只有 2 MB 左右的大小。
  • Spring 框架的核心特性是可以用于开发任何 Java 应用程序,但是在 Java EE 平台上构建 web 应用程序是需要扩展的。 Spring 框架的目标是使 J2EE 开发变得更容易使用,通过启用基于 POJO 编程模型来促进良好的编程实践。
  • 详细的DI,AOP这些概念可以自行去了解
  • 官网:https://spring.io/projects/spring-framework

2.Maven基本介绍:

有基础的同学可直接跳过

基于Spring+Quartz 搭建定时任务调度框架

  • Maven是一个项目管理和综合工具。Maven提供了开发人员构建一个完整的生命周期框架。开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周期。
  • 在多个开发团队环境时,Maven可以设置按标准在非常短的时间里完成配置工作。由于大部分项目的设置都很简单,并且可重复使用,Maven让开发人员的工作更轻松,同时创建报表,检查,构建和测试自动化设置。
  • 功能:构建,文档生成,报告,依赖,SCMs,发布,分发,邮件列表
  • 详细可了解:http://maven.apache.org/

3.基于idea使用Spring + Maven构建项目:

  • 新建项目

基于Spring+Quartz 搭建定时任务调度框架

  • 使用maven构建

基于Spring+Quartz 搭建定时任务调度框架

  • 填写信息

基于Spring+Quartz 搭建定时任务调度框架

  • 填写项目名称,路径

基于Spring+Quartz 搭建定时任务调度框架

  • 完成后是这样的

基于Spring+Quartz 搭建定时任务调度框架

  • 选择自动导入添加的依赖

基于Spring+Quartz 搭建定时任务调度框架

  • 集成Spring

将Spring所需要的依赖粘贴至pom文件中,等待依赖下载完成即可

     <!-- Spring依赖 -->        <!-- 1.Spring核心依赖 -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-core</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <!-- 2.Spring dao依赖 -->        <!-- spring-jdbc包括了一些如jdbcTemplate的工具类 -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-tx</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <!-- 3.Spring web依赖 -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-web</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>        <!-- 4.Spring test依赖:方便做单元测试和集成测试 -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>4.3.7.RELEASE</version>        </dependency>

集成 Quartz 任务调度框架

1.将Quartz的依赖粘贴至pom文件

        <!-- 引入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.2.1</version>        </dependency>

2.如果存在冲突,则需要手动排除包,正常像我们这样构建是没问题的

排除依赖使用

    // 排除示例  <dependency>        <groupId>org.apache.hadoop</groupId>        <artifactId>zookeeper</artifactId>        <version>3.3.1</version>        <exclusions>            <exclusion>                <groupId>jline</groupId>                <artifactId>jline</artifactId>            </exclusion>        </exclusions>    </dependency>

实战应用开发

这一章节我们将进入实战开发

1.首先编写作业

  • 创建一个job包

基于Spring+Quartz 搭建定时任务调度框架

  • 创建一个WorkJob类,并集成Quartz Job类
import org.quartz.*;import java.io.IOException;/** * @Description 任务作业 * @Date 2019/12/19 19:26 * @Created by Jason */// 保证任务一个接着一个执行@DisallowConcurrentExecutionpublic class WorkJob implements Job{    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {        System.out.println(" ===== 开始执行作业! ======");        JobDataMap dataMap = jobExecutionContext.getMergedJobDataMap();        String command = (String) dataMap.get("command");   // 接收JobDetail 传递过来的参数        executeWork(command); // 若需要个性化定制任务, 再这里写你的逻辑就行啦    }    // 执行具体作业    private void executeWork(String command) {        try {            // 可以执行,windows / linux 命令, 脚本等            System.out.println(command);            Process exec = Runtime.getRuntime().exec(command);            exec.waitFor();        } catch (IOException e) {            e.printStackTrace();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

我这里写的作业是执行windows或者Linux命令或脚本。需要JDBC操作的,直接在execute方法中写就OK了,数据库连接,sql参数这些可以用参数传递的方式传递过来。

2. 创建配置文件读取类

import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;import java.util.Properties;/** * @Description 配置文件操作类 * @Date 2019/12/19 20:09 * @Created by Jason */public class PropUtils {    private static String propPath;    private static Properties properties;    /**     * 读取配置文件     * @param path     * @throws IOException     */    private static void read(String path) throws IOException {        if (path!=null&&!"".equals(path)) {            if (properties == null) {                //可以读取任意路径的文件                properties = new Properties();                // 使用InPutStream流读取properties文件                BufferedReader bufferedReader =                        new BufferedReader(new FileReader(path));                properties.load(bufferedReader);            }        }        propPath = path;    }    /**     * 获取配置属性     * @param propPath     * @param key     * @return     */    public static String getPropValue(String propPath,String key) {        try {            read(propPath);        } catch (IOException e) {            e.printStackTrace();            System.out.println(">>>>>>>> 读取配置文件时发生异常!");        }        return properties.getProperty(key);    }}

创建配置文件读取类是为了,用户可以在任何目录下存放配置文件,程序可以灵活的读取用户的配置,比如用户的定时时间,cron表达式, 执行的命令等。这些不应该写死在程序中

这里我们读取的是properties配置文件

示例配置文件:

prop.properties

cron=*/5 * * * * ?command=notepad

cron代表时间表达式:每五秒执行一次任务,后面会详细说明

command代表需要执行的命令,notepad代表打开windows的记事本

3.编写Quartz任务管理器

import org.quartz.*;import org.quartz.impl.StdSchedulerFactory;import org.quartz.impl.matchers.GroupMatcher;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import java.util.Set;import java.util.TimeZone;/** * @Description 时间调度任务管理 * @Date 2019/12/19 19:52 * @Created by Jason */public class QuartzManager {    private final static Logger logger = LoggerFactory.getLogger(QuartzManager.class);    private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();    private static String JOB_GROUP_NAME = "MY_JOBGROUP_NAME";    private static String TRIGGER_GROUP_NAME = "MY_TRIGGERGROUP_NAME";    private static Scheduler scheduler;    // 北京时间    private static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";    /**     * 创建一个定时任务并启动     *     * @param jobName     * @param cls     * @param cron     * @param command     */    public static void addJob(String jobName, Class cls, String cron, String command) {        try {            Scheduler sched = gSchedulerFactory.getScheduler();            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobName, JOB_GROUP_NAME).build();            JobDataMap jobDataMap = job.getJobDataMap();            // 传递参数            jobDataMap.put("command", command);//            jobDataMap.put("timingExpId", timingExpId);            // 表达式调度构建器            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);            // 获取当前系统时间 转成 北京时间            Date currTime = new Date(System.currentTimeMillis());            Date date = changeTimeZone(currTime, TimeZone.getDefault(), TimeZone.getTimeZone("Asia/Shanghai"));            // 按新的cronExpression表达式构建一个新的trigger            Trigger trigger = TriggerBuilder.newTrigger()                    .withIdentity(jobName, TRIGGER_GROUP_NAME)                    .withSchedule(scheduleBuilder)                    .startAt(date)// 设置开始计时时间                    .build();            // 交给scheduler去调度            sched.scheduleJob(job, trigger);            // 启动            if (!sched.isShutdown()) {                sched.start();                System.out.println("新任务创建完毕,并启动 !jobName:[" + jobName + "]");                logger.info("新任务创建完毕,并启动 !jobName:[" + jobName + "]");            }        } catch (Exception e) {            logger.error("新任务创建、并启动 失败", e);        }    }    /**     * 移除一个定时任务     *     * @param jobName     */    public static void removeJob(String jobName) {        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);        try {            Scheduler sched = gSchedulerFactory.getScheduler();            Trigger trigger = (Trigger) sched.getTrigger(triggerKey);            if (trigger == null) {                return;            }            // 停止触发器            sched.pauseTrigger(triggerKey);            // 移除触发器            sched.unscheduleJob(triggerKey);            // 删除任务            sched.deleteJob(jobKey);            logger.info("移除任务,完毕!jobName:[" + jobName + "]");        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 查到当前任务的状态     *     * @param jobName     * @return NONE 无,     * NORMAL, 正常     * PAUSED, 暂停     * COMPLETE, 完成     * ERROR, 错误,     * BLOCKED 受阻;     */    public static String getTriggerState(String jobName) {        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, TRIGGER_GROUP_NAME);        String name = null;        try {            Scheduler sched = gSchedulerFactory.getScheduler();            Trigger.TriggerState triggerState = sched.getTriggerState(triggerKey);            name = triggerState.name();        } catch (Exception e) {            logger.error("获取任务状态失败!jobName:[" + jobName + "]", e);        }        return name;    }    /**     * 暂停一个任务     *     * @param jobName     */    public static void pauseJob(String jobName) {        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);        try {            Scheduler sched = gSchedulerFactory.getScheduler();            sched.pauseJob(jobKey);        } catch (Exception e) {            logger.error("暂停任务失败!jobName:[" + jobName + "]", e);        }    }    /**     * 恢复一个任务     *     * @param jobName     */    public static void resumeJob(String jobName) {        JobKey jobKey = JobKey.jobKey(jobName, JOB_GROUP_NAME);        try {            Scheduler sched = gSchedulerFactory.getScheduler();            sched.resumeJob(jobKey);        } catch (SchedulerException e) {            logger.error("恢复任务失败!jobName:[" + jobName + "]", e);        }    }    /**     * 获取定时任务调度中全部任务     */    public static void getAllJobs() {        try {            Scheduler sched = gSchedulerFactory.getScheduler();            List<String> triggerGroupNames = sched.getTriggerGroupNames();            for (String group : triggerGroupNames) {                Set<TriggerKey> triggerKeys = sched.getTriggerKeys(GroupMatcher.triggerGroupEquals(group));                for (TriggerKey triggerKey : triggerKeys) {                    String jobName = triggerKey.getName();                    String triggerState = getTriggerState(jobName);                }            }        } catch (Exception e) {            logger.info("获取任务调度管理器中全部任务失败", e);        }    }    /**     * 开启全部任务     */    public static void startJobs() {        try {            scheduler.start();        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 关闭全部任务     */    public void shutdownJobs() {        try {            if (!scheduler.isShutdown()) {                scheduler.shutdown();            }        } catch (Exception e) {            logger.error("关闭全部任务失败", e);        }    }    /**     * 删除定时任务     *     * @param jobName     * @param jobGroupName     * @param triggerName     * @param triggerGroupName     */    public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {        try {            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);            // 停止触发器            scheduler.pauseTrigger(triggerKey);            // 移除触发器            scheduler.unscheduleJob(triggerKey);            // 删除任务            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));        } catch (Exception e) {            throw new RuntimeException(e);        }    }    public void startJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {        try {            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 创建启动定时任务     * @param jobName           任务名称     * @param triggerName       定时任务名称     * @param triggerGroupName  定时任务组名称     * @param jobGroupName      任务组名称     * @param cron              cron表达式     * @param jobClass          jobClass     */    public void createTrgger(String jobName,String triggerName,String triggerGroupName,String jobGroupName,String cron,Class jobClass) {        try {            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();            jobDetail.getJobDataMap().put("messageId", "1");            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();            // 触发器名,触发器组            triggerBuilder.withIdentity(triggerName, triggerGroupName);            triggerBuilder.startNow();            // 触发器时间设定            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));            // 创建Trigger对象            CronTrigger trigger = (CronTrigger) triggerBuilder.build();            // 调度容器设置JobDetail和Trigger            scheduler.scheduleJob(jobDetail, trigger);            // 启动            if (!scheduler.isShutdown()) {                scheduler.start();            }        } catch (Exception e) {            logger.error("", e);        }    }    public Scheduler getScheduler() {        return scheduler;    }    public void setScheduler(Scheduler scheduler) {        this.scheduler = scheduler;    }    /**     * 获取更改时区后的日期     * @param date 日期     * @param oldZone 旧时区对象     * @param newZone 新时区对象     * @return 日期     */    public static Date changeTimeZone(Date date, TimeZone oldZone, TimeZone newZone) {        Date dateTmp = null;        if (date != null) {            int timeOffset = oldZone.getRawOffset() - newZone.getRawOffset();            dateTmp = new Date(date.getTime() - timeOffset);        }        return dateTmp;    }}

4.编写启动类

  • 方式1:jar包形式启动类

使用:

将项目打成jar包命令行启动:java -jar xxx.jar "D:/timer/timer/prop.properties"
import com.sskjdata.job.WorkJob;import com.sskjdata.manager.QuartzManager;import com.sskjdata.prop.PropUtils;/** * @Description TODO * @Date 2019/12/19 20:53 * @Created by Jason */public class MainTest {    public static void main(String[] args) {        if (args.length>0) {            // 获取文件配置属性, 参数: 配置文件地址 , 属性键            String command = PropUtils.getPropValue(args[0], "command");            String cron = PropUtils.getPropValue(args[0], "cron");            System.out.println("cron时间表达式:"+cron);            System.out.println("command执行命令:"+command);            QuartzManager.addJob("workJob",WorkJob.class,cron,command);        }else {            System.out.println("启动缺少参数! 必须参数:配置文件全路径名");        }    }}
  • 方式2:ide测试
import com.sskjdata.job.WorkJob;import com.sskjdata.manager.QuartzManager;import com.sskjdata.prop.PropUtils;/** * Created by Administrator on 2020/1/1 0001. */public class MainTest2 {    public static void main(String[] args) {        // 获取文件配置属性, 参数: 配置文件地址 , 属性键        String command = PropUtils.getPropValue("D:\timer\timer\prop.properties", "command");        String cron = PropUtils.getPropValue("D:\timer\timer\prop.properties", "cron");        System.out.println("cron时间表达式:"+cron);        System.out.println("command执行命令:"+command);        QuartzManager.addJob("workJob",WorkJob.class,cron,command);    }}
  • 方式3:打成依赖jar包,加入其他项目中使用

使用方式:

        // 获取文件配置属性, 参数: 配置文件地址 , 属性键        String command = PropUtils.getPropValue("文件地址", "配置文件属性");        String cron = PropUtils.getPropValue("文件地址", "配置文件属性");        System.out.println("cron时间表达式:"+cron);        System.out.println("command执行命令:"+command);        // 启动        QuartzManager.addJob("workJob",WorkJob.class,cron,command);

5.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-DEcDayofWeek:可出现", - * / ? 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 , - * /

测试验收项目以及项目总结

启动测试:

基于Spring+Quartz 搭建定时任务调度框架

看到执行并调用notepad成功,则说明成功了

打成可独立运行jar包, 依赖jar包

1

基于Spring+Quartz 搭建定时任务调度框架

2

基于Spring+Quartz 搭建定时任务调度框架

3

基于Spring+Quartz 搭建定时任务调度框架

4

基于Spring+Quartz 搭建定时任务调度框架

5

基于Spring+Quartz 搭建定时任务调度框架

6

基于Spring+Quartz 搭建定时任务调度框架

7

基于Spring+Quartz 搭建定时任务调度框架

8 一定要选src不然运行不了jar包

基于Spring+Quartz 搭建定时任务调度框架

9

基于Spring+Quartz 搭建定时任务调度框架

10

基于Spring+Quartz 搭建定时任务调度框架

基于Spring+Quartz 搭建定时任务调度框架

11 这样jar包就打包完成啦

基于Spring+Quartz 搭建定时任务调度框架

项目总结

  • 整体篇幅比较长,大家可以分批次阅读,或者目录跳转均可
  • 对于学习一个新的技术,首先要了解其概念,然后了解它是什么?能做什么?优点是什么?缺点是什么?核心是什么?
  • 实战使用,加深印象,在实战中发现问题解决问题,这样提升才是很快的
  • 记录笔记,笔记方便后期遗忘查看,有利于反复记忆,是一笔经验财富

最后希望此文对大家有所助益!

End.爱数据网专栏:大数据程序员笔记作者简介:分享在开发中遇到的问题和使用的知识技术,总结多年开发经验。个人CSDN主页: JavaBuilt

  • 我的微信公众号
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: