**
Quartz定时框架的使用、与spring整合的使用
**
一、Quartz是什么
Quartz是一个日常任务管理系统,也可以说成是一个定时器,他可以和任何其他软件系统集成或者一起使用。Quartz 相当“轻量”,并且需要非常少的步骤/配置,如果需求比较基本,Quartz确实非常容易使用。
Quartz 具有容错性,并且可以在你系统重起的时候持久化(记住)被纳入日程的任务。
二、为什么不使用Java.util.Timer?
- Java 定时器没有持久化机制。
- Java 定时器的日程管理不够灵活(只能设置开始时间、重复的间隔,设置特定的日
期、时间等) - Java 定时器没有使用线程池(每个 Java 定时器使用一个线程)
- Java 定时器没有切实的管理方案,你不得不自己完成存储、组织、恢复任务的措施。
三、如何避免一个job被并发激活
使 Job 类实现 StatefulJob 接口,而不是 Job 接口。
四、Quartz介绍
1、Quartz官网http://www.quartz-scheduler.org/
2、下载目录
使用时需要导入相应jar包,在下载目录里都有
3、入门案例
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
public class Demo1 {
public static void main(String[] args) throws SchedulerException {
//定时器对象
SchedulerFactory scheduler = new StdSchedulerFactory().getScheduler();
//开启定时器任务
scheduler.start();
//关闭定时器任务
scheduler.shutdown();
}
}
4、Quartz的使用
SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
sched.start();
JobDetail jobDetail = new JobDetail("myJob",null,DumbJob.class);
Trigger trigger = TriggerUtils.makeHourlyTrigger(); //每个小时激活一次
trigger.setStartTime(TriggerUtils.getEvenHourDate(new Date()));//在下一个
小时启动。
trigger.setName("myTrigger");
sched.scheduleJob(jobDetail, trigger);
五、Quartz定时框架simpleTrigger开发使用
如果需要让任务只在某个时刻执行一次,或者,在某个时刻开始,然后按照某个时间
间隔重复执行,simpleTrigger就可以满足你的需求
SimpleTrigger 有几个不同的构造函数
第一种
public SimpleTrigger(String name,
String group,
Date startTime,
Date endTime,
int repeatCount,
long repeatInterval)
SimpleTrigger Example 1 – 从现在 10 秒钟后开始触发,并且只触发一次。
long startTime = System.currentTimeMillis() + 10000L;
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
null,
new Date(startTime),
null,
0,
0L);
SimpleTrigger Example 2 – 创建一个立即触发的触发器,并且每隔 60 秒钟触发一
次,直到永远。
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
null,
new Date(),
null,
SimpleTrigger.REPEAT_INDEFINITELY,
60L * 1000L);
SimpleTrigger Example 3 - 创建一个立即触发的触发器,并且每隔 10 秒重复一次,
直到 40 秒钟以后。
long endTime = System.currentTimeMillis() + 40000L;
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
"myGroup",
new Date(),
new Date(endTime),
SimpleTrigger.REPEAT_INDEFINITELY,
10L * 1000L);
SimpleTrigger Example 4 - 创建一个触发器,开始触发时间为 2018 年 3 月 17 上
午 午 10 :30 分,并且重复 5 次。(总共触发 6 次)每次延迟 30 秒钟。
java.util.Calendar cal = new java.util.GregorianCalendar(2018, cal.MARCH, 17);
cal.set(cal.HOUR, 10);
cal.set(cal.MINUTE, 30);
cal.set(cal.SECOND, 0);
cal.set(cal.MILLISECOND, 0);
Data startTime = cal.getTime()
SimpleTrigger trigger = new SimpleTrigger("myTrigger",
null,
startTime,
null,
5,
30L * 1000L);
当然还有另一种
1、重要接口说明
1)Job:工作任务,你要做什么,Job一般自定义实现,由接口JobDetail来创建
2)Trigger执行工作任务,什么时间执行,多久执行一次常用的有:SimpleTrigger、CronTrigger
2)Scheduler定时器对象,开启定时任务
2、编写工作任务
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloJob implements Job {
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("hello,quartz!");
}
}
3、编写定时任务代码
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class Demo2 {
public static void main(String[] args) throws SchedulerException {
//定时器对象
SchedulerFactory scheduler = new StdSchedulerFactory().getScheduler();
//定义一个工作对象
JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
//定义触发器
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();
scheduler.scheduleJob(job, trigger);
//开启定时任务
scheduler.start();
}
}
1)对象TriggerBuilder启动任务时间
2)对象SimpleScheduleBuilder进行简单任务重复执行
3)repeatSecondly…() 多少秒后重复执行
4)repeatMinutely…() 多少分钟后重复执行
5)repeatHourly…() 多少小时后重复执行
六、quartz定时框架CronTrigger开发使用
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import cn.itcast.quartz.demo2.HelloJob;
public class Demo5 {
public static void main(String[] args) throws SchedulerException {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();
Trigger trigger = TriggerBuilder.newTrigger().forJob("job1", "group1").startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0 1 23 ? * SUN")).build();
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
}
1)Cron表达式取值
字段名 允许的值 允许的特殊字符
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日 1-31 , - * ? / L W C
月 1-12 or JAN-DEC , - * /
周几 1-7 or SUN-SAT , - * ? / L C #
年 (可选字段) empty, 1970-2099 , - * /
2)具体说明
Day-of-month 和 Day-of-week必须有一个指定为?
“?”字符:表示不确定的值
“,”字符:指定数个值
“-”字符:指定一个值的范围
“/”字符:指定一个值的增加幅度。n/m表示从n开始,每次增加m
“L”字符:用在日表示一个月中的最后一天,用在周表示该月最后一个星期X
6L表示该月最后一个星期五
“W”字符:指定离给定日期最近的工作日(周一到周五)
“#”字符:表示该月第几个周X。6#3表示该月第3个周五
字符“W”只允许日期域出现。这个字符用于指定日期的最近工作日。例如:如果你在日期域中写 “15W”,表示:这个月15号最近的工作日。所以,如果15号是周六,则任务会在14号触发。如果15好是周日,则任务会在周一也就是16号触发。如果是在日期域填写“1W”即使1号是周六,那么任务也只会在下周一,也就是3号触发,“W”字符指定的最近工作日是不能够跨月份的。字符“W”只能配合一个单独的数值使用,不能够是一个数字段,如:1-15W是错误的。
“L”和“W”可以在日期域中联合使用,LW表示这个月最后一周的工作日。
字符“#”只允许在星期域中出现。这个字符用于指定本月的某某天。例如:“6#3”表示本月第三周的星期五(6表示星期五,3表示第三周)。“2#1”表示本月第一周的星期一。“4#5”表示第五周的星期三。
字符“C”允许在日期域和星期域出现。这个字符依靠一个指定的“日历”。
它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
3)举例:
每隔5秒执行一次:*/5 * * * * ?
每隔1分钟执行一次:0 */1 * * * ?
每天23点执行一次:0 0 23 * * ?
每天凌晨1点执行一次:0 0 1 * * ?
每月1号凌晨1点执行一次:0 0 1 1 * ?
每月最后一天23点执行一次:0 0 23 L * ?
在26分、29分、33分执行一次:0 26,29,33 * * * ?
每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
每天中午12点触发 0 0 12 * * ?
每天上午10:15触发 0 15 10 ? * *
每天上午10:15触发 0 15 10 * * ?
每天上午10:15触发 0 15 10 * * ? *
2018年的每天上午10:15触发 0 15 10 * * ? 2018
在每天下午2点到下午2:59期间的每1分钟触发 0 * 14 * * ?
在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14 * * ?
在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 0 0/5 14,18 * * ?
在每天下午2点到下午2:05期间的每1分钟触发 0 0-5 14 * * ?
每年三月的星期三的下午2:10和2:44触发 0 10,44 14 ? 3 WED
周一至周五的上午10:15触发 0 15 10 ? * MON-FRI
每月15日上午10:15触发 0 15 10 15 * ?
每月最后一日的上午10:15触发 0 15 10 L * ?
每月的最后一个星期五上午10:15触发 0 15 10 ? * 6L
2015年至2018年的每月的最后一个星期五上午10:15触发 0 15 10 ? * 6L 2015-2018
每月的第三个星期五上午10:15触发 0 15 10 ? * 6#3
七、quartz定时框架和spring集成使用
1、建立war包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>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
2、配置web.xml
<!-- spring配置文件位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- spring核心监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3、配置applicationContext.xml
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">
<context:component-scan base-package="cn.itcast" />
<bean id="helloJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="cn.itcast.job.HelloJob"/>
<property name="jobDataAsMap">
<map>
<entry key="timeout" value="5"/>
</map>
</property>
</bean>
<!-- ======================== 调度触发器 ======================== -->
<bean id="cronTriggerBean" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="helloJob"></property>
<property name="cronExpression" value="0 54 * * * ?"></property>
</bean>
<!-- ======================== 调度工厂 ======================== -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory" ref="jobFactory" />
<property name="triggers">
<list>
<ref bean="cronTriggerBean" />
</list>
</property>
</bean>
</beans>
4、代码编写
HelloService
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public void sayHello() {
System.out.println("hello,quartz service !");
}
}
helloJob
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import cn.itcast.service.HelloService;
public class HelloJob extends QuartzJobBean {
@Autowired
private HelloService helloService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
helloService.sayHello();
}
}
JobFactory
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;
}
}
八、quartz和spring整合Bean无法注入问题
需要在Scheduler中自定义JobFactory
AutowireCapableBeanFactory这个接口一般在applicationContext的内部是较少使用的,它的功能主要是为了装配applicationContext管理之外的Bean
上面代码已经解决了这个问题