quartz开源任务调度框架知识总结

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名称自动注入,必须通过明确引用注入

猜你喜欢

转载自blog.csdn.net/a656678879/article/details/81739075