Java&Quartz实现任务调度
1.Quartz的作用
定时自动执行任务
2.预备
相关包官方网站
quartz2.2.1
quartz-jobs2.2.1
POM文件
<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>
3.Quartz核心
3.1.Job接口
被调度的任务,只有一个方法execute(JobExecutionContext xontext),Job运行时的信息保存在JobDataMap中
3.2.JobDetail类
实现Job接口,用来描述Job的相关信息,包含Name,Group,JobDataMap等
3.3 JobExecutionContext类
定时程序执行的run-time的上下文环境,用于得到Job的名字、配置的参数等
3.3 JobDataMap类
用来描述一个作业的参数,参数可以为金和基本类型或者某个对象的引用
3.3 JobListener接口
监听作业状态
3.3 TriggaerListener接口
监听触发器状态
3.3 JobStore
3.3.Tigger抽象类
触发器,描述执行Job的触发规则,有SimpleTrigger和CronTrigger两个子类
3.3.1.SimpleTrigger类
继承自Trigger类,每隔xx毫秒/秒执行一次,主要实现固定一次或者固定时间周期类
任务的触发
3.3.2.CronTrigger类
继承自Trigger类,使用Cron表达式,实现各种复杂时间规则
调度方案,如每天的某个时间,或每周的某几天触发执行之类
3.4.Calendar包
一些日历特定时间点的集合,包内包含以下几个类
3.4.1 BaseCalendar类
3.4.2 AnnualCalendar类
排除每一年中指定的一天或者多天
3.4.3 CalendarComparator类
3.4.4 CronCalendar类
使用表达式排除某时间段不执行
3.4.5 DailyCalendar类
指定的时间范围内每天不执行
3.4.6 HolidayCalendar类
排除节假日
3.4.7 MonthlyCalendar类
配出月份中的数天
3.4.8 WeeklyCalendar类
排除没周中的一天或者多天
3.5.Scheduler类
任务调度器,代表一个Quartz独立容器。
Scheduler可以将JobDetail和Trigger绑定,当Trigger触发时,对应的Job就会被执行,Job和Trigger是1:n(一对多)的关系
3.6Misfire类
错误的任务,本该执行单没有执行的任务调度
4.实现
1.单任务实现
1.定义一个任务,新建任务类继承自Job类
package com;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class DemoJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
2.新建类执行这个任务(SimpleTrigger)
package com;
import java.util.Date;
import org.quartz.DateBuilder;
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 QuartzDemo {
public void simpleRun() throws SchedulerException {
// 创建一个调度器工厂
SchedulerFactory factory = new StdSchedulerFactory();
//任务执行时间
//Date runTime = DateBuilder.evenMinuteDate(new Date());
Date runTime = DateBuilder.evenSecondDateAfterNow();
// 新建JobDetail对象并绑定一个任务
JobDetail jobDetail = JobBuilder.newJob(DemoJob.class)
.withIdentity("demo_job", "demo_group")
.build();
// 定义调度规则
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("demo_trigger", "demo_group")
//.startNow()//立即执行
.startAt(new Date())//设置触发开始的时间
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(1)//时间间隔
.withRepeatCount(5)//重复次数(n+1),比如这里将执行6次
).build();//生成触发器
// 从工厂获取一个调度器对象
Scheduler scheduler = factory.getScheduler();
//绑定触发器和任务
scheduler.scheduleJob(jobDetail,trigger);
System.out.println(jobDetail.getKey() + " 运行在: " + runTime);
scheduler.start();
}
public static void main(String[] args) {
QuartzDemo demo = new QuartzDemo();
try {
demo.simpleRun();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
2.多任务实现
- 测试任务类
新建两个DemoJonOne和DemoJobTwo,都实现Job接口,内容如下
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date())+" Runed "+getClass().getName());
}
2.新建QuartzUtil类,内容如下
package com;
import org.quartz.Job;
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 QuartzUtil {
private final static String JOB_GROUP_NAME = "QUARTZ_JOBGROUP_NAME";// 任务组
private final static String TRIGGER_GROUP_NAME = "QUARTZ_TRIGGERGROUP_NAME";// 触发器组
/**
* 添加任务的方法
*
* @param jobName 任务名
* @param triggerName 触发器名
* @param jobClass 执行任务的类
* @param seconds 间隔时间
* @throws SchedulerException
*/
public static void addJob(String jobName, String triggerName, Class<? extends Job> jobClass, int seconds)
throws SchedulerException {
// 创建一个SchedulerFactory工厂实例
SchedulerFactory sf = new StdSchedulerFactory();
// 通过SchedulerFactory构建Scheduler对象
Scheduler sche = sf.getScheduler();
// 用于描述Job实现类及其他的一些静态信息,构建一个作业实例
JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, JOB_GROUP_NAME).build();
// 构建一个触发器,规定触发的规则
Trigger trigger = TriggerBuilder.newTrigger()// 创建一个新的TriggerBuilder来规范一个触发器
.withIdentity(triggerName, TRIGGER_GROUP_NAME)// 给触发器起一个名字和组名
.startNow()// 立即执行
.withSchedule(
SimpleScheduleBuilder
.simpleSchedule()
.withIntervalInSeconds(seconds)// 时间间隔 // 单位:秒
.repeatForever()// 一直执行
).build();// 产生触发器
//绑定触发器和任务
sche.scheduleJob(jobDetail, trigger);
// 启动
sche.start();
}
public static void main(String[] args) {
try {
// 添加第一个任务 每隔10秒执行一次
QuartzUtil.addJob("job1", "trigger1", DemoJobOne.class, 2);
// 添加第二个任务 每隔20秒执行一次
QuartzUtil.addJob("Job2", "trigger2", DemoJobTwo.class, 5);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
以上方法属于手动调用,如果是web项目中就不同了
添加POM
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
package servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.quartz.SchedulerException;
import com.DemoJobOne;
import com.DemoJobTwo;
import com.QuartzUtil;
public class InitServlet extends HttpServlet {
private static final long serialVersionUID = 8507188690597926975L;
/**
* 因为我们不需要处理请求与响应的消息操作,所以这个地方只留一个初始化的操作就行了,用以执行任务调度的入口
*/
public void init() throws ServletException {
try {
// 添加第一个任务 每隔2秒执行一次
QuartzUtil.addJob("job1", "trigger1", DemoJobOne.class, 2);
// 添加第二个任务 每隔5秒执行一次
QuartzUtil.addJob("Job2", "trigger2", DemoJobTwo.class, 5);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
2.注册servlet
<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>servlet.InitServlet</servlet-class>
<!-- 设置优先级 -->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>InitServlet</servlet-name>
<url-pattern>/InitServlet</url-pattern>
</servlet-mapping>
3.复杂规则任务调度(CronTrigger)
在每分钟的1-30秒执行示例
package com;
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;
public class CronTriggerDemo {
public static void main(String[] args) throws SchedulerException {
SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
JobDetail job = JobBuilder
.newJob(DemoJobOne.class)
.withIdentity("job","group")
.build();
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("trigger", "group")
.startNow().withSchedule(
CronScheduleBuilder
.cronSchedule("1-30 * * * * ?")
).build();
scheduler.scheduleJob(job,trigger);
scheduler.start();
}
}
5.Cron表达式
规则
格式
s M h d m w [y]
s:seconds,取值0-59,允许- * /;
M:minutes,取值0-59,允许- * /;
h:hour,取值0-23,允许- * /;
d:day of month,取值1-31,允许- * ? / L W;
m:month,取值1-12/JAN-DEC,允许- * /;
w:day of week,取值1-7/SUN-SAT,允许- * ? / L #;
y:year,可选,取值empty、1970-2099,允许- * /;
符号解释
、 指定枚举值,如在秒字段使用10、12,则表示只有第10秒和第12秒执行
- 指定区间范围,配合使用,如在小时字段使用10-12,表示在10、11、12时都会触发
* 代表所有值,单独使用,如在秒字段使用,表示每秒触发
? 代表不确定值,单独使用,不用关心的值
/ 用于递增触发,配合使用,n/m,从n开始,每次增加m,如在秒字段设置5/15,表示从第5秒开始,每15秒触发一次
L 表示最后,单独使用,如在秒字段使用,代表第59秒触发,如果在前面加上数字,则表示该数据的最后一个,如在周字段使用6L,则表示本月最后一个周五
W 表示最近的工作日,不会跨月,比如30W,30号是周六,则不会顺延至下周一来执行,如在月字段使用15W,则表示到本月15日最近的工作日(周一到周五)
# 用来指定x的第n个工作日,如在周字段使用6#3则表示该月的第三个星期五
月取值
一月:JAN/0
二月:FEB/1
三月:MAR/2
四月:APR/3
五月:MAY/4
六月:JUN/5
七月:JUL/6
八月:AUG/7
九月:SEP/8
十月:OCT/9
十一月:NOV/10
十二月:DEC/11
周取值
周日:SUN/1
周一:MON/2
周二:TUE/3
周三:WED/4
周四:THU/5
周五:FRI/6
周六:SAT/7
示例
0/20 * * * * ? 每20秒执行一次
1-30 * * * * ? 在1-30秒执行
15 0/2 * * * ? 偶数分钟的第15秒执行
0 0/2 8-17 * * ? 从8时到17时 ,每个偶数分钟执行一次
0 0/3 17-23 * * ? 从17时到23时,每3分钟运行一次
0 0 10am 1,15 * ? 每个月的1号和15号的上午10点 运行
0,30 * * ? * MON-FRI 周一至周五,每30秒运行一次
0,30 * * ? * SAT,SUN 周六、周日,每30秒运行一次
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分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 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 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)
6.Spring整合Quartz
需要Spring-context-support包支持,POM如下
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.5.RELEASE</version>
</dependency>
新建两种Job测试类-->DemoSimpleJob类和DemoCronJob类,并继承自QuartzJobBean,代码如下
package com;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class DemoJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()) + " 输出自:" + getClass().getName());
}
}
配置spring bean如下
<?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"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!--org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean -->
<!-- 配置任务 -->
<bean id="demoCronJob"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.DemoCronJob" />
</bean>
<bean id="demoSimpleJob"
class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.DemoSimpleJob" />
</bean>
<!-- <property name="jobDataAsMap"> -->
<!-- 配置触发器 -->
<bean id="simpleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="jobDetail" ref="demoSimpleJob" />
<property name="startDelay" value="1000" />
<property name="repeatInterval" value="2000" />
</bean>
<bean id="cornTrigger"
class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="demoCronJob" />
<property name="cronExpression" value="1-30 * * * * ?" />
</bean>
<!-- 配置调度器 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cornTrigger" />
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
启动
package com;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
有待补充