为什么需要使用任务调度?
在企业级应用中,经常会制定一些“计划任务”,即在某个时间点做某件事情,核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作。
任务调度涉及多线程并发、线程池维护、运行时间规则解析、运行现场的保护以恢复等方面。
举个例子:在中秋节整点的时候给所有用户发送一封祝福邮箱,在我们手工的情况下去完成那是非常麻烦的,难道我大半夜调个闹钟给你发一封邮件?但是使用Quartz之后我们可以设置一个预定义的时间执行发送邮件这项任务。
Quartz几乎可以集成在任何Java应用程序中 - 从最小的独立应用程序到最大的电子商务系统,并且Quartz是一个开源的项目,他可以让计划的程序在指定的某个时间中进行完成,Quartz可以用来创建简单到复杂的日程安排成千上万个。
Quartz官网:http://www.quartz-scheduler.org/
Quartz框架的核心概念
任务调度涉及多线程并发、线程池维护、运行时间规则解析、运行现场的保护以恢复等方面。
1)任务:任务就是要执行的工作,Quartz中给我们提供了Job接口,我们只需要实现这个接口重写接口中的execute(JobExecutionContext context)方法就可以完成一个任务的定义
2)触发器:顾名思义触发那么肯定就是任务需要触碰到某种规则,Quartz提供Trigger类及其子类支持触发器功能。
3)调度器:Quartz提供了Scheduler接口,它是Quartz独立运行的容器。将工作任务和触发器注册到调度器中,保证任务可以在正确的时间执行。
目前官方最新版本是Quartz 2.2.3 .tar.gz,在这里下载的就是最新版的,在lib文件夹中找到jar包。
Quartz框架使用流程:
首先这是个帮助我们执行某些任务的框架那么首先肯定要创建一个任务。
package org.miya.quartz.job;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;public class MyJob implements Job{ /** * Job接口中只有一个execute()方法,在实现类中实现该方法以执行具体任务 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { //通过参数JobExecutionContext可以获取调度上下文的各种信息,如:任务名称等 System.out.println("My Job ..."); System.out.println("任务 : " + context.getJobDetail().getKey()); System.out.println("任务名称: " + context.getJobDetail().getKey().getName()); System.out.println("任务分组名称 : " + context.getJobDetail().getKey().getGroup()); } }
调用过程
package org.miya.quartz.job;import java.util.Date;import org.quartz.JobBuilder;import org.quartz.JobDetail;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.SimpleScheduleBuilder;import org.quartz.Trigger;import org.quartz.TriggerBuilder;import org.quartz.impl.StdSchedulerFactory;public class Main { public static void main(String[] args) throws SchedulerException { //1.创建一个工作任务 JobDetail jobDetail = JobBuilder.newJob(MyJob.class) .withIdentity("jobName", "JobGroupName") .build(); //2.创建一个触发器 并且使用SimpleScheduleBuilder定义规则 SimpleScheduleBuilder ssb = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5)//设置每5秒执行一次 .repeatForever(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("TriggerName", "TriggerGroupName") .withSchedule(ssb) .startAt(new Date())//设置开始时间 .endAt(new Date())//设置结束时间 .build(); //3.创建调度器 Scheduler scheduler = new StdSchedulerFactory().getScheduler(); scheduler.scheduleJob(jobDetail,trigger);//设置任务与触发时机 scheduler.start();//启动调度器 } }
现在我们归类以下再Quartz中我们常使用的接口
1)Job:执行任务调度的组件要实现的接口。
2)JobDetail:用来定义Job的实例
3)JobBuilder:用来定义或创建JobDetail的实例
4)Tigger:定义一个Job何时被执行的组件,也称触发器。
5)TriggerBuilder:用来创建Tigger的实例
6)Scheduler:跟任务调度相关的接口
调度的声明周期,起于SchedulerFactory的创建,止于shutdown()方法。
我们还可以向任务提供参数,再任务中可以通过Job调度上下文来获取参数。
JobDetail job = JobBuilder.newJob(JobDemo2.class) .withIdentity("jobDemo2", "group1") .usingJobData("name", "Hello world!") .usingJobData("age", 20) .build();
//获取传递过来的参数 JobDataMap dataMap = context.getJobDetail().getJobDataMap(); System.out.println("name:" + dataMap.getString("name")); System.out.println("age:" + dataMap.getInt("age"));
这样获取参数,每一个参数我们都需要获取一遍比较麻烦,我们可以再我们的任务类中给name,age这两个属性定义getset方法任务提供参数方式不变,这样也是可以的。
Quartz框架默认的JobFactory实现类在初始化job实例对象时会自动地调用这些setter方法。
CronTrigger
CronTrigger相比SimpleTrigger更常用,当你需要一个基于日历般概念的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间。
CronTrigger是基于Cron表达式的触发器。Cron表达式实际上是由7个子表达式组成的字符串,描述了时间表的详细信息,这些信息使用空格分开。
我们要使用CronTrigger就必须要掌握Cron表达式,Cron表达式由6~7个由空格分隔的时间元素组成。第7个元素可选。
位置 | 字段含义 | 范围 | 允许的特殊字符 |
1 | 秒 | 0~59 | * / |
2 | 分钟 | 0~59 | * / |
3 | 小时 | 0~23 | * / |
4 | 月份中的哪一天 | 1~31 | * / ? L |
5 | 月份 | 1~12 或 JAN~DEC | * / |
6 | 星期几 | 1~7 或 SUN~SAT | * / ? L # |
7 | 年份 | 1970~2099 | * / |
Cron表达式有几个特殊的字符:
特殊符号 | 说明 | |
通用 | “ - ” | 中划线,表示一个范围 |
“ , ” | 使用逗号间隔的数据,表示一个列表 | |
“ * ” | 表示每一个值,它可以用于所有字段。在小时字段表示每小时。 | |
限定 | “ ? ” | 该字符仅用于“月份中的哪一天”字段和“星期几”字段,表示不指定值 |
“ / ” | 通常表示为x/y,x为起始值,y表示值的增量。 | |
“ L ” | 表示最后一天,仅在日期和星期字段中使用 | |
“ # ” | 只能用于“星期几”字段,表示这个月的第几个周几。例如:“6#3”指这个月第三个周五 |
Cron表达式示例
Cron表达式 | 含义 |
0 0 8-12 ? * MON-FRI | 每个工作日的8点到12点 |
0 15 4 * * ? | 每天凌晨4点15分 |
30 0 0 1 1 ? 2014 | 2014年1月1日凌晨过30秒 |
0 0 14 1,10,20 * ? * | 每月的1号、10号、20号的下午2点 |
0 0 17 L * ? | 每月最后一天17:00运行 |
0 0 10 ? * 6L | 每月最后一个星期五10:00运行 |
0 0/5 15,17 * * ? | 每天15点到16点每5分钟运行一次,每天17点到18点每5分钟运行一次 |
0 30 10 ? * 6#3 2013 | 2013年每月的第三个星期五上午10:30触发 |
创建CronTrigger触发器
//周一至周日每天每个小时每5秒钟执行一次。 String cronString = "0/5 * * ? * 1-7"; Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .withSchedule(CronScheduleBuilder.cronSchedule(cronString)) .forJob(jobDetail) .build();
CronTrigger和SimpleTrigger的对比:
SimpleTrigger 应用场景:固定时间间隔的调度任务(例如:每隔2小时执行1次),使用方式:通过设置触发器的属性:开始时间、结束时间、重复次数、重复间隔等
CronTrigger 应用场景:指定时间点的调度任务(例如:每天凌晨1:00执行1次),使用方式:通过定义Cron表达式
在Spring中如何使用Quartz?
Spring对Quartz提供了支持,对Quartz的核心类进行了封装,使开发人员更便捷地实现任务调度
使用声明的方式配置计划任务,大大简化了操作步骤,而且也降低了代码耦合
在spring中只需要一个普通类,并不需要实现Job接口
package org.spring.quartz;public class MySpringJob { public void job(){ System.out.println("Job工作 。。。"); } }
<!-- 定义任务 --> <bean id="myjob" class="org.spring.quartz.MySpringJob"></bean> <!-- 添加任务 --> <bean id="jobDetail1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="myjob" /> <property name="targetMethod" value="job" /> </bean> <!-- 配置SimpleTrigger任务调度器 --> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="jobDetail1" /> <property name="startDelay" value="1000" /> <property name="repeatInterval" value="2000" /> </bean> <!-- 配置CronTrigger任务调度器 --> <bean id="crontestJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="jobDetail1" /> <property name="cronExpression" value="0/2 * * ? * 1-7" /> </bean> <!-- 配置触发器开始 --> <bean id="startQuartz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="simpleTrigger" /> </list> </property> </bean>
总结
使用Quartz框架实现任务调度的核心是创建任务(Job)、触发器(Trigger)和调度器(Scheduler)。
Quartz的两种常用触发器:SimpleTrigger和CronTrigger。
Spring对Quartz的核心组件进行封装,包括JobDetailBean、SimpleTriggerBean、CronTriggerBean、SchedulerFactoryBean,使用可以更高效地实现任务调度。
通过MethodInvokingJobDetailFactoryBean,允许直接由类方法配置成工作任务
共同学习,写下你的评论
评论加载中...
作者其他优质文章