Quartz框架从入门到实战

一、什么是quartz作业调度?
Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能,比另一个调度框架Timer强大了许多,但是它使用也不难,下面我废话不多说,直奔主题。
二、quartz的体系结构。
1.quartz中使用了一下几种设计模式。

  • Builer模式
  • Factory模式
  • 组件模式
  • 链式写法

2.三个主要的概念

  • 调度器 :Quartz框架的核心是调度器。调度器负责管理Quartz应用运行时环境。调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件。Quartz不仅仅是线程和线程池管理。为确保可伸缩性,Quartz采用了基于多线程的架构。启动时,框架初始化一套worker线程,这套线程被调度器用来执行预定的作业。这就是Quartz怎样能并发运行多个作业的原理。Quartz依赖一套松耦合的线程池管理部件来管理线程环境。
  • 任务:这个很简单,就是我们自己编写的业务逻辑,交给quartz帮我们执行 。
  • 触发器:简单的讲就是调度作业,什么时候开始执行,什么时候结束执行。

    3.quartz的体系结构
    quartz框架至少有三百多个类组成,这里我们重点介绍几个它的核心部分

  • JobDetail:quartz每次都会直接创建一个JobDetail,同时创建一个Job实例,它不直接接受一个Job的实例,但是它接受一个Job的实现类,通过new instance()的反射方式来实例一个Job,在这里Job是一个接口,我们需要自己编写类去实现这个接口。下面我们会讲到这个接口。

  • Trigger : 它由SimpleTrigger和CronTrigger组成,SimpleTrigger实现类似Timer的定时调度任务,CronTrigger可以通过cron表达式实现更复杂的调度逻辑·。
  • Scheduler:调度器,JobDetail和Trigger可以通过Scheduler绑定到一起。

    4.quartz重要组成部分

    1).Job接口:可以通过实现该就接口来实现我们自己的业务逻辑,该接口只有execute()一个方法,我们可以通过下面的方式来实现Job接口来实现我们自己的业务逻辑

public class HelloJob implements Job{

    public void execute(JobExecutionContext context) throws JobExecutionException {
    //编写我们自己的业务逻辑
    }

2).JobDetail:
每次都会直接创建一个JobDetail,同时创建一个Job实例,它不直接接受一个Job的实例,但是它接受一个Job的实现类,通过new instance()的反射方式来实例一个Job.可以通过下面的方式将一个Job实现类绑定到JobDetail中

JobDetail jobDetail=JobBuilder.newJob(HelloJob.class).
                withIdentity("myJob", "group1")
                .build();

3)JobBuiler:
主要是用来创建jobDeatil实例
4)JobStore:
绑定了Job的各种数据
5)trigger:前文讲到它主要用来执行Job实现类的业务逻辑的,我们可以通过下面的代码来创建一个Trigger实例:(这里我们会看到cron表达式,可以先不用,我们后面会介绍)

CronTrigger trigger = (CronTrigger) TriggerBuilder
                .newTrigger()
                .withIdentity("myTrigger", "group1")    //创建一个标识符
                .startAt(date)//什么时候开始触发
                //每秒钟触发一次任务
                .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))

                .build();

6)Scheduler:创建Scheduler有两种方式
通过StdSchedulerFactory来创建

SchedulerFactory sfact=new StdSchedulerFactory();
Scheduler scheduler=sfact.getScheduler();

通过DirectSchedulerFactory来创建

DiredtSchedulerFactory factory=DirectSchedulerFactory.getInstance();
Scheduler scheduler=factory.getScheduler();

Scheduler 配置参数一般存储在quartz.properties中,我们可以修改参数来配置相应的参数。通过调用getScheduler()方法就能创建和初始化调度对象。

Scheduler的主要函数介绍:

Date schedulerJob(JobDetail,Trigger trigger);返回最近触发的一次时间
void standby()暂时挂起
void shutdown()完全关闭,不能重新启动了
shutdown(true)表示等待所有正在执行的job执行完毕之后,再关闭scheduler
shutdown(false)即直接关闭scheduler

在这里我们不得不提一下quartz.properties这个资源文件,在org.quartz这个包下,当我们程序启动的时候,它首先会到我们的根目录下查看是否配置了该资源文件,如果没有就会到该包下读取相应信息,当我们咋实现更复杂的逻辑时,需要自己指定参数的时候,可以自己配置参数来实现。下面我们简单看一下这个资源文件:

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

该资源文件主要组成部分:
①调度器属性
②线程池属性
③作业存储设置
④插件设置

调度器属性:
org.quartz.scheduler.instanceName属性用来区分特定的调度器实例,可以按照功能用途来给调度器起名。
org.quartz.scheduler.instanceId属性和前者一样,也允许任何字符串,但这个值必须是在所有调度器实例中是唯一的,尤其是在一个集群当中,作为集群的唯一key,假如你想quartz帮你生成这个值的话,可以设置我Auto

线程池属性:
threadCount设置线程的数量

threadPriority设置线程的优先级

org.quartz.threadPool.class 线程池的实现

作业存储设置:
描述了在调度器实例的声明周期中,job和trigger信息是怎么样存储的

插件配置:
满足特定需求用到的quartz插件的配置

5.监听器
监听器顾名思义,就是对事件进行监听并且加入自己相应的业务逻辑,主要有以下三个监听器分别对Job,Trigger,Scheduler进行监听。

  • JobListener
  • TriggerListener
  • SchedulerListener

三、Cron表达式
在这里,我们着重讲解一下cron表达式,quartz之所以能够实现更加复杂的业务逻辑,主要在依赖于cron表达式。
cron表达式编写的顺序一次是”秒 分 时 日 月 周 年”。
在这里我们可以看两张图片就能了解到cron表达式的基本语法了。
这里写图片描述

这里写图片描述

这里写图片描述

通过上面几张照片的介绍及实例讲解相信都对cron表达式有所了解吧,讲了这么多理论知识下面我们进入实战部分。

四、quartz框架实战
这里我们简要讲解一个我们这个实例,我们先实现Job接口来编写我们相应的业务逻辑,然后创建JobDetail实例将job实现类绑定,接着创建Trigger,然后创建Scheduler实例将JobDetail实例和Trigger绑定到一起。

不好意思,在这里我们漏掉了一个重要的属性(不好意思 ,hhhhhhhhh)
JobDataMap主要用啦设置和获取一些自定义的参数,比如JobDetail和trigger的name和group属性。它可以通过两种方式来获取和设置参数。

1.第一种方式,通过map的方式获取JobDataMap中的值,JobDataMap获取自定义的一些参数,下面这段代码获取的是我们即将要写的例子中的JobDetail和trigger的一些自定义参数(在实例中我们将省略这些代码,避免复杂性)

JobDataMap jobDataMap=context.getJobDetail().getJobDataMap();
        JobDataMap tDataMap=context.getTrigger().getJobDataMap();
//      JobDataMap jobDataMap2=context.getMergedJobDataMap();       //把两个map合并
        String jobMsg=jobDataMap.getString("msg");
        float jobFloat=jobDataMap.getFloat("JobFloatValue");
        String triMsg=tDataMap.getString("msg");
        double triDouble=tDataMap.getDouble("TriggerDoubleValue");
        System.out.println("job message are:"+jobMsg+" "+jobFloat);
        System.out.println("trigger message are:"+triMsg+" "+triDouble);

2.第二种方式,创建每一个key的属性,并且创建set,get方法,然后在Job实现类中通过set和get方法来获取信息。

private String msg;
    private float JobFloatValue;
    private double TriggerDoubleValue;
        public void setJobFloatValue(float jobFloatValue) {
        JobFloatValue = jobFloatValue;
    }
    public float getJobFloatValue() {
        return JobFloatValue;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public String getMsg() {
        return msg;
    }
    public void setTriggerDoubleValue(double triggerDoubleValue) {
        TriggerDoubleValue = triggerDoubleValue;
    }
    public double getTriggerDoubleValue() {
        return TriggerDoubleValue;
    }

好了,终于讲完了,下面我们继续我们的实例。
下面是Job实现类

public class HelloJob implements Job{
//JobExecutionContext 提供了调度上下文的各种参数
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //编写具体的逻辑关系
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //打印当前时间
        SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current time is :"+simpleDateFormat.format(new Date()));
        System.out.println("Hello World");
        }

触发器:

public class HelloSchedule{
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        //创建一个JobDetail实例,与HelloJob class绑定

        JobDetail jobDetail=JobBuilder.newJob(HelloJob.class).
                withIdentity("myJob", "group1")
                .build();
        Date date=new Date();
        //使用CronTrigger每秒钟触发一次任务
        CronTrigger trigger = (CronTrigger) TriggerBuilder
                .newTrigger()
                .withIdentity("myTrigger", "group1")    //创建一个标识符
                .startAt(date)
                //每秒钟触发一次任务
                .withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ? *"))
                .build();
        //创建一个schedule实例
        SchedulerFactory schedulerFactory=new StdSchedulerFactory();
        Scheduler scheduler=schedulerFactory.getScheduler();
        scheduler.start();
        //打印当前时间
        SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Current time is :"+simpleDateFormat.format(new Date()));
        System.out.println("Current time is :"+scheduler.scheduleJob(jobDetail, trigger))

    }
}

至此,我们完成了我们的第一个实例,每个一秒中执行我们的业务,即每个一秒中打印一次信息。

五,整合spring框架
quartz的强大还在于它可以和各种框架进行无缝整合,当然包括最流行的spring框架。下面我们来整合spring。
首先建立maven项目导入相关依赖,在这里我们不讲如何搭建spring框架,读者需要有spring的知识。
pom.xml文件

 <dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.rest-assured/spring-mock-mvc -->
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>spring-mock-mvc</artifactId>
        <version>3.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.3.12.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.3.0</version>
    </dependency>
  </dependencies>

然后,我们在src/main/java目录下创建几个bean类,分别对应着我们的JobDetail,Trigger,和Scheduler,这里显而易见,我们通过spring容器来实例化bean,然后在spring中的配置文件中来用Scheduler把JobDetail和Trigger来绑定到一起,来实现业务逻辑。
Mybean.java:

@Component("myBean")
public class MyBean {
    public void printMessage(){
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("It is mybean "+sdf.format(date));
    }
}

FristSchedulerJob.java

@Component("fristSchedulerJob")
public class FristSchedulerJob extends QuartzJobBean{
    private AnotherBean  anotherBean;

    public void setAnotherBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }
    @Override
    protected void executeInternal(JobExecutionContext context)
            throws JobExecutionException {
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("It is my frist job "+sdf.format(date));
        this.anotherBean.printAotherMessage();
    }
}

AnotherBean.java

@Component("anotherBean")
public class AnotherBean {
    public void printAotherMessage(){
        System.out.println("AnotherBean");
    }
}

下面我们看spring的配置文件,在这里需要说明的是我们通过controller对web请求进行管理控制,quartz在后台进行调度业务。

<?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:p="http://www.springframework.org/schema/p"
    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">
        <!-- 配置渲染器 -->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <!-- 配置视图的前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 配置视图的后缀 -->
        <property name="suffix" value=".jsp"/>
        </bean>

        <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <property name="targetObject" ref="myBean"/>
            <property name="targetMethod" value="printMessage"/>
        </bean>

        <bean id="fristComplexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
            <property name="jobClass" value="cn.shinelon.quartz.FristSchedulerJob"/>
            <property name="jobDataMap">
                <map>
                    <entry key="anotherBean" value-ref="anotherBean"/>
                </map>
            </property>
            <property name="durability" value="true"/>
        </bean>
        <!-- 距离当前时间 1秒钟之后开始执行,每隔两种执行一次-->
        <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
            <property name="jobDetail" ref="simpleJobDetail"/>
            <property name="startDelay" value="1000"/>
            <property name="repeatInterval" value="2000"/>
        </bean>
        <!-- 每隔5秒钟执行一次 -->
        <bean id="myCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
            <property name="jobDetail" ref="fristComplexJobDetail"/>
            <property name="cronExpression" value="0/5 * * ? * *"/>
        </bean>

       <!-- 配置SchedulerFactoryBean -->
       <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="jobDetails">
                <list>
                    <ref bean="simpleJobDetail"/>
                    <ref bean="fristComplexJobDetail"/>
                </list>
            </property>
                <property name="triggers">
                <list>
                    <ref bean="simpleTrigger"/>
                    <ref bean="myCronTrigger"/>
                </list>
            </property>
       </bean>
        <!-- 配置注解扫描器 -->
        <context:component-scan base-package="cn.shinelon"></context:component-scan>
</beans>

在这里我们省略controller层和web.xml文件的代码,相信大家都能够自己完成。
至此,我们完成了对spring的整合,怎么样,有没有感受到quartz框架的强大(反正我没有感受到,哈哈哈哈哈,累死了,敲了一下午代码,赶紧去吃饭,溜了溜了)。

这里写图片描述

  • 33
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值