Quartz任务调度框架
使用场景:我们经常在网站上看到一些限时活动,当活动未结束时,活动显示进行时,当活动到达结束日期时我们要将活动设置为已结束,如下图:
怎样让程序自动实现这个效果呢
Quartz就是启动定时任务的框架,下载地址:http://www.quartz-scheduler.org/
我们在maven中使用需要导入以下依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
使用Quartz我们需要掌握三个对象的使用:
1. Scheduler: 定时器,用于管理JobDetail和Trigger。
2. Job: 执行的任务,需要实现org.quartz.Job接口
3. Trigger:触发器,指定任务执行的时间和循环的方式, 什么时间执行, 多久执行一次
触发器有两种:
1. SimpleTrigger: 设置开始时间,设置间隔时间。特点: 间隔时间必须是相同的情况下。
2. CronTrigger: 表达式
Quartz入门:
案例一:我们的工作任务是输出一句话:”Hello Quartz!!!”,现在要让这句话每3秒就输出一次;
编写工作任务:
package cn.itcast.demo2;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job{
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Hello Quartz!!!");
}
}
编写定时任务代码(SimpleTrigger):
package cn.itcast.demo1;
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 QuartzDemo1{
public static void main(String[] args) throws SchedulerException {
//定时器对象
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//定义工作对象
JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
//定义触发器
Trigger tigger = TriggerBuilder.newTrigger().
withIdentity("trigger1", "group1").startAt(new Date()).
withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))
.build();
//开启定时任务
scheduler.scheduleJob(job, tigger);
scheduler.start();
//关闭定时任务
// scheduler.shutdown();
}
}
ps:1.对象 TriggerBuilder 启动任务时间
startNow 立即启动
startAt (Date) 指定时间启动
对象 SimpleScheduleBuilder 进行简单任务重复执行
repeatSecondly …() 多少秒后重复执行
repeatminutely …() 多少分钟后重复执行
repeatHourly …() 多少小时后重复执行
2.Job类需要实现Job接口
案例二:按照按照某个时间安排输出:”Hello Quartz!!!”;
CronTrigger的使用:CronTrigger使用较为复杂,主要是表达式的写法:
一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素,分别为:
按顺序依次为
1 秒(0~59)
2 分钟(0~59)
3 小时(0~23)
4 天(1~31)
5 月(1~12)
6 星期(1~7 (1=SUN )或 SUN,MON,TUE,WED,THU,FRI,SAT)
7.年份(1970-2099),可选
一些通配符的使用:
“*”字符代表所有可能的值
“/”字符用来指定数值的增量
“?”字符仅被用于天(月)和天(星期)两个子表达式,表示不指定值
“L” 字符仅被用于天(月)和天(星期)两个子表达式,它是单词“last”的缩写
“W”字符代表着平日(Mon-Fri),并且仅能用于日域中。它用来指定离指定日的最近的一个平日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。
“C”代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
下图列出了个字段能出现的值:
下面还有一些我们在工作中可能会用到的表达式:
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”
了解完CronTrigger的表达式后,我们这个案例就很简单了,比如我现在的时间是2018/9/3的20:22分,我需要输出”Hello Quartz!!!”,要求是让这句话在今天20:23分的时候每隔2秒就输出一次,代码如下:
package cn.itcast.demo2;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import cn.itcast.demo2.HelloJob;
public class QuartzDemo2{
public static void main(String[] args) throws SchedulerException {
// 定时器对象
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 定义一个工作对象
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1").build();
// 定义触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 23 20 3 9 ?"))
.build();
scheduler.scheduleJob(job, trigger);
// 开启定时任务
scheduler.start();
}
}
ps需要注意的点:
day-of-month 和 day-of-week 只能设置一个值,另一个值写 ?
当我们不需要制定年份是,年份一般省略;
**
Quartz整合Spring框架
**
我们在工作中使用Quartz框架往往是整合spring来使用,这样复杂的执行代码就不用我们编写了,我们只需要关心配置:
JobDetialFactoryBean:
<!--job-->
<bean id="exampleJob"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="exampleJob的全类名" />
</bean>
TriggerFactoryBean:
<!--simpleTrigger-->
<bean id="simpleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="exampleJob" />
<!--程序加载就开始运行-->
<property name="startDelay" value="0" />
<!--每1分钟执行一次-->
<property name="repeatInterval" value="60000" />
</bean>
<!--cromTrigger-->
<bean id="cronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="exampleJob" />
<!--每天早上6点执行-->
<property name="cronExpression" value="0 0 6 * * ?" />
</bean>
SchedulerFactoryBean:
<!--scheduler-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory" ref="jobFactory" />
<property name="triggers">
<list>
<ref bean="simpleTrigger"/>
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
问题:我们在代码中往往是在web层注入service层对象,再调用service层对象执行方法,但是在 Job 中 spring 管理的 Bean 无法注入:
package cn.itcast.quartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.quartz.service.HelloService;
public class HelloJob implements Job{
@SuppressWarnings("unused")
@Autowired
private HelloService helloService;
public void execute(JobExecutionContext context) throws JobExecutionException {
//这里会出现空指针异常
helloService.sayHello();
}
}
解决: 需要在 Scheduler 中自定义 JobFactory类:
package cn.itcast.quartz.service;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Service;
@Service("jobFactory")
public class JobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle)
throws Exception {
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
在配置文件中修改:
上一篇: 猜数字游戏