目录:
- 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/
简单总结:
- 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 是最受欢迎的企业级 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基本介绍:
有基础的同学可直接跳过
- Maven是一个项目管理和综合工具。Maven提供了开发人员构建一个完整的生命周期框架。开发团队可以自动完成项目的基础工具建设,Maven使用标准的目录结构和默认构建生命周期。
- 在多个开发团队环境时,Maven可以设置按标准在非常短的时间里完成配置工作。由于大部分项目的设置都很简单,并且可重复使用,Maven让开发人员的工作更轻松,同时创建报表,检查,构建和测试自动化设置。
- 功能:构建,文档生成,报告,依赖,SCMs,发布,分发,邮件列表
- 详细可了解:http://maven.apache.org/
3.基于idea使用Spring + Maven构建项目:
- 新建项目
- 使用maven构建
- 填写信息
- 填写项目名称,路径
- 完成后是这样的
- 选择自动导入添加的依赖
- 集成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包
- 创建一个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 , - * /
测试验收项目以及项目总结
启动测试:
看到执行并调用notepad成功,则说明成功了
打成可独立运行jar包, 依赖jar包
1
2
3
4
5
6
7
8 一定要选src不然运行不了jar包
9
10
11 这样jar包就打包完成啦
项目总结
- 整体篇幅比较长,大家可以分批次阅读,或者目录跳转均可
- 对于学习一个新的技术,首先要了解其概念,然后了解它是什么?能做什么?优点是什么?缺点是什么?核心是什么?
- 实战使用,加深印象,在实战中发现问题解决问题,这样提升才是很快的
- 记录笔记,笔记方便后期遗忘查看,有利于反复记忆,是一笔经验财富
最后希望此文对大家有所助益!
End.爱数据网专栏:大数据程序员笔记作者简介:分享在开发中遇到的问题和使用的知识技术,总结多年开发经验。个人CSDN主页: JavaBuilt
- 我的微信公众号
- 微信扫一扫
- 我的微信公众号
- 微信扫一扫
评论