quartz开源任务调度框架知识总结
任务调度的实现总结
quartz 时间表达式之Cron表达式详解
任务调度框架Quartz知识要点
作为一个优秀的开源调度框架,Quartz 具有以下特点:
1、强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
2、 灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
3、分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
4.作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。
5.Quartz专用词汇:
scheduler :任务调度器
trigger :触发器,用于定义任务调度时间规则
job :任务,即被调度的任务
misfire :错过的,指本来应该被执行但实际没有被执行的任务调度
6.Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器
7.trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务。
8.Quartz 中主要提供了四种类型的 trigger:
SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger
9.job 用于表示被调度的任务。两种类型:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次
触发的任务被执行完之后,才能触发下一次执行,job默认无状态(无状态任务一般指可以并发的任务,即任务之间是独立的,不会互相干扰)
10.Job 主要有两种属性:volatility 和 durability,其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger
关联的时候任务是否被保留。两者都是在值为 true 的时候任务被持久化或保留。job : trigger -> 1 : n
11.scheduler 由 scheduler 工厂创建:DirectSchedulerFactory 或者 StdSchedulerFactory。StdSchedulerFactory使用广泛。
Scheduler 主要有三种:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。
12.“spring-context-support-3.2.4.RELEASE.jar” 此包是spring根据quartz中的主要类进行再次封装成具有bean风格的类;
“quartz-2.2.1.jar” quartz的核心包
使用前提说明:
在Spring中使用Quartz有两种方式实现:
第一种是任务类继承QuartzJobBean,【利用JobDetailBean包装QuartzJobBean子类(即Job类)的实例】
第二种则是在配置文件里定义任务类和要执行的方法,类和方法仍然是普通类。【利用MethodInvokingJobDetailFactoryBean工厂Bean包装普通的Java对象(即Job类)】
很显然,第二种方式远比第一种方式来的灵活。
第一种方式:QuartzJobBean ,实现 executeInternal(JobExecutionContext
jobexecutioncontext)方法,此方法就是被调度任务的执行体
不用继承QuartzJobBean,需要指定一下两个属性:
targetObject:指定包含任务执行体的Bean实例。
targetMethod:指定将指定Bean实例的该方法包装成任务的执行体
步骤简介
步骤说明:
java类:
1.定义任务实现类(java)【可选为继承QuartzJobBean,和不继承QuartzJobBean】
配置文件:
1.配置任务详情JobDetail有两种方式
- 方式一:使用JobDetailBean,任务类必须实现Job(继承QuartzJobBean)接口
- 方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job(继承QuartzJobBean)接口,通过targetMethod指定调用方法
2. trigger 任务调度触发器,主要用于定义jobDetail什么时候执行有两种方式:
- 方式一:简单触发器SimpleTrigger,只能指定任务执行以什么样的频率执行,但无法制定精确的执行时间
- 方式二:任务触发器CronTrigger,则既可以执行简单触发器所制定的以频率来执行的时间,也可以制定复杂的时间计划来执行
3.schedulerFactory 任务调度工厂;用于调度各个任务触发器
步骤
第一步:定义作业类
第一种方式,作业类继承自特定的基类:org.springframework.scheduling.quartz.QuartzJobBean
package com.pkk.ehcache.task;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
import com.pkk.ehcache.constand.SysConstand;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import com.pkk.ehcache.entity.User;
import com.pkk.ehcache.service.UserServcice;
import com.pkk.ehcache.util.OutPutLoggerContext;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author peikunkun
* @version V1.0
* @Title: frames
* @Package com.pkk.ehcache.task
* @Description: <>
* @date 2018/4/4 11:15
*/
@Component
public class UserQuartzJob extends QuartzJobBean {
/**
* 多长时间后开始
*/
private int startTime = 0;
/**
* 在配置文件中会一句配置文件,进行注入数据
*
* @param startTime
*/
public void setStartTime(int startTime) {
this.startTime = startTime;
}
@Resource(name = "userServcice")
private UserServcice userServcice;
/**
* 执行次数
*/
private int exeueCount = 0;
/**
* @author kunzai
* @version V1.0
* @Title: 在配置文件中使用JobDetailFactoryBean的bean对象时才需要继承QuartzJobBean
* @Package com.pkk.ehcache.task
* @Description: <>
* @date 18-4-6下午5:13
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.print("注入的时间为:" + startTime);
int randomIn = new Random().nextInt(20);
List<User> users = userServcice.findUser(randomIn);
OutPutLoggerContext.log("Quartz任务调度-任务一-注入数据为:" + startTime + ",现在时刻是" + new SimpleDateFormat(SysConstand.FORMAT).format(new Date()) + "executeInternal(需要继承QuartzJobBean)-第" + exeueCount++ + "次查询,插叙用户id为" + randomIn + "查询的结果为:" + users);
}
}
方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job(继承QuartzJobBean)接口,通过targetMethod指定调用方法
package com.pkk.ehcache.task;
import com.pkk.ehcache.constand.SysConstand;
import com.pkk.ehcache.entity.User;
import com.pkk.ehcache.service.UserServcice;
import com.pkk.ehcache.util.OutPutLoggerContext;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
* @author peikunkun
* @version V1.0
* @Title: frames
* @Package com.pkk.ehcache.task
* @Description: <>
* @date 2018/4/4 11:15
*/
@Component
public class UserQuartzJobNoExtend {
@Resource(name = "userServcice")
private UserServcice userServcice;
/**
* 执行次数
*/
private int exeueCount = 0;
/**
* @author kunzai
* @version V1.0
* @Title: 任务调度框架
* @Package com.pkk.ehcache.task
* @Description: <任务调度任务二>
* @date 18-4-6下午3:47
*/
public void exeueTaskMethodTwo() {
int randomIn = new Random().nextInt(20);
List<User> users = userServcice.findUser(randomIn);
OutPutLoggerContext.log("Quartz任务调度-任务二:exeueTaskMethodTwo-现在时刻是" + new SimpleDateFormat(SysConstand.FORMAT).format(new Date()) + ",第" + exeueCount++ + "次查询,插叙用户id为" + randomIn + "查询的结果为:" + users);
}
}
第二步:spring配置文件中配置作业类JobDetailBean
方式一:使用JobDetailBean,任务类必须实现Job(继承QuartzJobBean)接口
<!--第一步,配置JobDetail,有两种方式,主要作用是,告诉Spring的工作类和工作方法等任务相关-->
<!--方式一:使用JobDetailBean,任务类必须实现Job接口 -->
<bean id="jobDetailName1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="name" value="userQuartzJob1"/>
<!--制定实现任务的对象是谁 -->
<property name="jobClass" value="com.pkk.ehcache.task.UserQuartzJob"/>
<!--注入数据,需要set方法-->
<property name="jobDataAsMap">
<map>
<!--往实体中注入数据startTime为2000-->
<entry key="startTime">
<value>2000</value>
</entry>
</map>
</property>
</bean>
*方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job(继承QuartzJobBean)接口,通过targetMethod指定调用方法
*
<!--定义任务bean-->
<bean id="userQuartzJobNoExtend" class="com.pkk.ehcache.task.UserQuartzJobNoExtend"/>
<!--推荐第二种方式,方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="jobDetailName2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="name" value="userQuartzJob2"/>
<!--任务对象-->
<property name="targetObject" ref="userQuartzJobNoExtend"/>
<!--任务的方法-->
<property name="targetMethod" value="exeueTaskMethodTwo"/>
<!--是否可以并发执行,false表示上一个任务没有执行前,不执行下一个任务-->
<property name="concurrent" value="false"/>
</bean>
第三步:trigger 任务调度触发器,主要用于定义jobDetail什么时候执行有两种方式:
方式一:简单触发器SimpleTrigger,只能指定任务执行以什么样的频率执行,但无法制定精确的执行时间
<!--第二步4:trigger 任务调度触发器,也有两种方式主要用于定义jobDetail什么时候执行。触发器最常用的有两种:简单触发器SimpleTrigger 和任务触发器CronTrigger -->
<!--第一种方式:简单触发器SimpleTrigger-只可以设置简单的周期,不可以设定时间-->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<!--执行那个任务一-->
<property name="jobDetail" ref="jobDetailName1"/>
<!--延迟时间,单位是毫秒-->
<property name="startDelay" value="2000"/>
<!--30秒的循环周期-->
<property name="repeatInterval" value="30000"/>
</bean>
*方式二:任务触发器CronTrigger,则既可以执行简单触发器所制定的以频率来执行的时间,也可以制定复杂的时间计划来执行
*
<!--第二种方式-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--指定任务的详情-->
<property name="jobDetail" ref="jobDetailName2"/>
<!--指定任务执行的周期(每年月日的每时分的每6秒16秒26秒36秒,增幅为10秒)-->
<property name="cronExpression" value="6/10 * * * * ?"/>
</bean>
第四步:schedulerFactory 任务调度工厂;用于调度各个任务触发器
注意:MyJobScheduler自定义的处理类,主要是解决,任务类中Service注入不成功的问题节约,当然你也可以在配置文件中定义一个ServiceBean,然后通过配置jobDataAsMap配置项,传递进行注入数据
这里说下我遇到的问题,我需要在一个action中进行任务的调度,但是在任务类中直接加注解注入service报空指针。
后来网上查才知道spring是将bean放在ApplicationContext中的。
而quartz初始化是自己的JobContext,不同于Spring的ApplicationContext,所以无法直接注入。
<!-- 第四步:schedulerFactory 任务调度工厂;用于调度各个任务触发器-->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
<ref bean="simpleTrigger"/>
</list>
</property>
<!-- 这里配置任务不随spring容器初始化而自动启动 -->
<!--<property name="autoStartup" value="false"/>-->
<!--解决任务类中注入Service注入不成功-->
<!--这里说下我遇到的问题,我需要在一个action中进行任务的调度,但是在任务类中直接加注解注入service报空指针。
后来网上查才知道spring是将bean放在ApplicationContext中的。
而quartz初始化是自己的JobContext,不同于Spring的ApplicationContext,所以无法直接注入。-->
<property name="jobFactory">
<bean class="com.pkk.ehcache.task.util.MyJobScheduler"/>
</property>
</bean>
解决任务类中注入Service注入不成功
package com.pkk.ehcache.task.util;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
/**
* @author kunzai
* @version V1.0
* @Title: MyJobScheduler
* @Package com.pkk.ehcache.task.util
* @Description: <主要是解决Quartz注入不成功,而重写SpringBeanJobFactory类的createJobInstance方法,实现自动注入>
* @date 18-4-6下午10:42
*/
public class MyJobScheduler extends SpringBeanJobFactory {
@Autowired
private AutowireCapableBeanFactory beanFactory;
/**
* 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire。
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
beanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
完整的配置文件
主要是怕上面乱,这里特此贴出一个完整的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<description>这个主要是用于Quartz框架的配置</description>
<!--定义任务bean-->
<bean id="userQuartzJobNoExtend" class="com.pkk.ehcache.task.UserQuartzJobNoExtend"/>
<!--第一步,配置JobDetail,有两种方式,主要作用是,告诉Spring的工作类和工作方法等任务相关-->
<!--方式一:使用JobDetailBean,任务类必须实现Job接口 -->
<bean id="jobDetailName1" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="name" value="userQuartzJob1"/>
<!--制定实现任务的对象是谁 -->
<property name="jobClass" value="com.pkk.ehcache.task.UserQuartzJob"/>
<!--注入数据,需要set方法-->
<property name="jobDataAsMap">
<map>
<!--往实体中注入数据startTime为2000-->
<entry key="startTime">
<value>2000</value>
</entry>
</map>
</property>
</bean>
<!--推荐第二种方式,方式二:使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="jobDetailName2" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="name" value="userQuartzJob2"/>
<!--任务对象-->
<property name="targetObject" ref="userQuartzJobNoExtend"/>
<!--任务的方法-->
<property name="targetMethod" value="exeueTaskMethodTwo"/>
<!--是否可以并发执行,false表示上一个任务没有执行前,不执行下一个任务-->
<property name="concurrent" value="false"/>
</bean>
<!--第二步4:trigger 任务调度触发器,也有两种方式主要用于定义jobDetail什么时候执行。触发器最常用的有两种:简单触发器SimpleTrigger 和任务触发器CronTrigger -->
<!--第一种方式:简单触发器SimpleTrigger-只可以设置简单的周期,不可以设定时间-->
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<!--执行那个任务一-->
<property name="jobDetail" ref="jobDetailName1"/>
<!--延迟时间,单位是毫秒-->
<property name="startDelay" value="2000"/>
<!--30秒的循环周期-->
<property name="repeatInterval" value="30000"/>
</bean>
<!--第二种方式-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--指定任务的详情-->
<property name="jobDetail" ref="jobDetailName2"/>
<!--指定任务执行的周期(每年月日的每时分的每6秒16秒26秒36秒,增幅为10秒)-->
<property name="cronExpression" value="6/10 * * * * ?"/>
</bean>
<!-- 第三步:schedulerFactory 任务调度工厂;用于调度各个任务触发器-->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
<ref bean="simpleTrigger"/>
</list>
</property>
<!-- 这里配置任务不随spring容器初始化而自动启动 -->
<!--<property name="autoStartup" value="false"/>-->
<!--解决任务类中注入Service注入不成功-->
<!--这里说下我遇到的问题,我需要在一个action中进行任务的调度,但是在任务类中直接加注解注入service报空指针。
后来网上查才知道spring是将bean放在ApplicationContext中的。
而quartz初始化是自己的JobContext,不同于Spring的ApplicationContext,所以无法直接注入。-->
<property name="jobFactory">
<bean class="com.pkk.ehcache.task.util.MyJobScheduler"/>
</property>
</bean>
</beans>
注意事项:
在Spring配置和Quartz集成内容时,有两点需要注意
1、在中不能够设置default-lazy-init=”true”,否则定时任务不触发,如果不明确指明default-lazy-init的值,默认是false。
2、在中不能够设置default-autowire=”byName”的属性,否则后台会报org.springframework.beans.factory.BeanCreationException错误,这样就不能通过Bean名称自动注入,必须通过明确引用注入