Spring Boot 技术探索

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

33、Spring Boot之Quartz的使用

平台环境:

名称

版本号

Mac OS X

10.15.2

JDK

1.8.0_201

Apache Maven

3.6.0

IntelliJ IDEA

2019.3 (Ultimate Edition)

Spring Boot

2.2.2.RELEASE

 

  Spring Boot内置的定时方案。

pom.xml

<dependencies>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
   </dependency>

   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
         <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
         </exclusion>
      </exclusions>
   </dependency>
</dependencies>

 

启动类增加启用定时注解@EnableScheduling

package com.example.demo;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;


@SpringBootApplication
@EnableScheduling
public class DemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(DemoApplication.class, args);
    }
}

 

新建定时类SchedulerTask

package com.example.demo;


import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


@Component
public class SchedulerTask
{
    private int count = 0;


    @Scheduled(cron = "*/3 * * * * ?") // 每隔3秒执行一次
    private void cronTest()
    {
        System.out.println("This scheduler task is running at " + (count++));
    }
}

 

cron参数说明

cron一共有七位,最后一位是年,Spring Boot 定时方案中只设置六位:

  • 第一位,表示秒,取值 0 ~ 59;
  • 第二位,表示分,取值 0 ~ 59;
  • 第三位,表示小时,取值 0 ~ 23;
  • 第四位,日期天/日,取值 1 ~ 31;
  • 第五位,日期月份,取值 1~12;
  • 第六位,星期,取值 1 ~ 7,星期一,星期二...,注,不是第 1 周、第 2 周的意思,另外,1 表示星期天,2 表示星期一;
  • 第七位,年份,可以留空,取值 1970 ~ 2099。

 

cron 中,还有一些特殊的符号,含义如下:

  • (*)星号,可以理解为每的意思,每秒、每分、每天、每月、每年...。
  • (?)问号,问号只能出现在日期和星期这两个位置,表示这个位置的值不确定,每天 3 点执行,因此第六位星期的位置,是不需要关注的,就是不确定的值;同时,日期和星期是两个相互排斥的元素,通过问号来表明不指定值,比如 1 月 10 日是星期一,如果在星期的位置另指定星期二,就前后冲突矛盾了。
  • (-)减号,表达一个范围,如在小时字段中使用“10 ~ 12”,则表示从 10 到 12 点,即 10、11、12。
  • (,)逗号,表达一个列表值,如在星期字段中使用“1、2、4”,则表示星期一、星期二、星期四。
  • (/)斜杠,如 x/y,x 是开始值,y 是步长,比如在第一位(秒),0/15 就是从 0 秒开始,每隔 15 秒执行一次,最后就是 0、15、30、45、60,另 */y,等同于 0/y。

 

常用例子:

  • 0 0 3 * * ? :每天 3 点执行;
  • 0 5 3 * * ?:每天 3 点 5 分执行;
  • 0 5 3 ? * *:每天 3 点 5 分执行,与上面作用相同;
  • 0 5/10 3 * * ?:每天 3 点的 5 分、15 分、25 分、35 分、45 分、55 分这几个时间点执行;
  • 0 10 3 ? * 1:每周星期天,3 点 10 分执行,注,1 表示星期天;
  • 0 10 3 ? * 1#3:每个月的第三个星期,星期天执行,# 号只能出现在星期的位置。

 

新建定时类SchedulerTask2

package com.example.demo;


import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


import java.text.SimpleDateFormat;
import java.util.Date;


@Component
public class SchedulerTask2
{
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");


    @Scheduled(fixedRate = 1000) // 上一次开始执行时间点之后1秒再执行
    public void fixedRateTest()
    {
        System.out.println("现在时间1:" + dateFormat.format(new Date()));
    }


    @Scheduled(fixedDelay = 1000) // 上一次执行完毕时间点之后1秒再执行
    public void fixedDelayTest() throws InterruptedException
    {
        Thread.sleep(2000);
        System.out.println("现在时间2:" + dateFormat.format(new Date()));
    }


    @Scheduled(initialDelay=500, fixedRate=2000) // 第一次延迟500毫秒后执行,之后按fixedRate的规则每2秒执行一次
    public void initialDelayTest()
    {
        System.out.println("现在时间3:" + dateFormat.format(new Date()));
    }
}

 


  什么是Quartz?

  Quartz全称为Quartz Job Scheduling Library, 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。

 

  更多介绍可以参看官网

  http://www.quartz-scheduler.org

What is the Quartz Job Scheduling Library?

Quartz is a richly featured, open source job scheduling library that can be integrated within virtually any Java application - from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; jobs whose tasks are defined as standard Java components that may execute virtually anything you may program them to do. The Quartz Scheduler includes many enterprise-class features, such as support for JTA transactions and clustering.

Quartz is freely usable, licensed under the Apache 2.0 license.

 

Quartz的4个核心概念

1、 Job表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:

void execute(JobExecutionContext context)

2、JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。

3、Trigger代表一个调度参数的配置,什么时候去调。

4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。

 

上面说的太抽象,举个例子。

1、Job相当于要做的具体的工作,例如吃饭、睡觉、打豆豆。

2、JobDetail相当于一张A4纸,上面清清楚楚的写明了需要做的Job。

3、Trigger相当于压在4A纸上面的闹钟,闹钟一响即开始纸上的工作。

4、Scheduler是桌子,桌子上摆满了一组组的闹钟和A4纸。

 

新建工程springBootDemo33B

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

 

Quartz提供了两种实现定时任务的方法

一、简单定时任务

SampleJob

package com.example.demo.job;


import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;


public class SampleJob extends QuartzJobBean
{
    private String name;


    public void setName(String name)
    {
        this.name = name;
    }


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException
    {
        System.out.println("Hello " + this.name + "!");
    }
}

 

SampleScheduler

package com.example.demo.scheduler;


import com.example.demo.job.SampleJob;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class SampleScheduler
{
    @Bean
    public JobDetail sampleJobDetail()
    {
        return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").usingJobData("name", "World").storeDurably().build();
    }


    @Bean
    public Trigger sampleJobTrigger()
    {
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever();


        return TriggerBuilder.newTrigger().forJob(sampleJobDetail()).withIdentity("sampleTrigger").withSchedule(scheduleBuilder).build();
    }
}

 

启动spring项目,可以看到每隔两秒钟,打印出来一次Hello World!

Hello World!
Hello World!
Hello World!

 

二、高级定时任务

定义两个Job

ScheduledJob

package com.example.demo.job;


import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;


public class ScheduledJob implements Job
{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException
    {
        System.out.println("Scheduled Job1 is running ...");
    }
}

 

ScheduledJob2

package com.example.demo.job;


import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;


public class ScheduledJob2 implements Job
{
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException
    {
        System.out.println("Scheduled Job2 is running ...");
    }
}

 

 

CronSchedulerJob

package com.example.demo.scheduler;


import com.example.demo.job.ScheduledJob;
import com.example.demo.job.ScheduledJob2;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;


@Component
public class CronSchedulerJob
{
    private SchedulerFactoryBean schedulerFactoryBean;


    @Autowired
    public void setSchedulerFactoryBean(SchedulerFactoryBean schedulerFactoryBean)
    {
        this.schedulerFactoryBean = schedulerFactoryBean;
    }


    public void startSchedule() throws SchedulerException
    {
        // 通过工厂类获取Scheduler
        Scheduler scheduler = schedulerFactoryBean.getScheduler();


        // 为Scheduler设置JobDetail与CronTrigger
        scheduleJob1(scheduler);
        scheduleJob2(scheduler);
    }


    private void scheduleJob1(Scheduler scheduler) throws SchedulerException
    {
        JobDetail jobDetail = JobBuilder.newJob(ScheduledJob.class).withIdentity("job1", "group1").build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/2 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }


    private void scheduleJob2(Scheduler scheduler) throws SchedulerException
    {
        JobDetail jobDetail = JobBuilder.newJob(ScheduledJob2.class).withIdentity("job2", "group2").build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger2", "group2").withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

 

MyStartupRunner

package com.example.demo;


import com.example.demo.scheduler.CronSchedulerJob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;


@Component
public class MyStartupRunner implements CommandLineRunner
{
    public CronSchedulerJob scheduleJobs;


    @Autowired
    public void setScheduleJobs(CronSchedulerJob scheduleJobs)
    {
        this.scheduleJobs = scheduleJobs;
    }


    @Override
    public void run(String... args) throws Exception
    {
        scheduleJobs.startSchedule();
        System.out.println(">>>>>>>>>>>>>>>定时任务开始执行<<<<<<<<<<<<<");
    }
}

 

启动项目前,先把SampleJob的executeInternal内容注释掉,不然影响效果。

 

执行结果

>>>>>>>>>>>>>>>定时任务开始执行<<<<<<<<<<<<<
Scheduled Job2 is running ...
Scheduled Job1 is running ...
Scheduled Job1 is running ...
Scheduled Job1 is running ...
Scheduled Job2 is running ...

 

除了使用CommandLineRunner的方式启动定时任务,还可以通过@Scheduled注解的方式按照规则启动。

 

SchedulerListener

package com.example.demo;


import com.example.demo.scheduler.CronSchedulerJob;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


@Configuration
@EnableScheduling
@Component
public class SchedulerListener
{
    public CronSchedulerJob scheduleJobs;


    @Autowired
    public void setScheduleJobs(CronSchedulerJob scheduleJobs)
    {
        this.scheduleJobs = scheduleJobs;
    }


    @Scheduled(cron = "0 53 15 * * *") // 每天的15:53分开始执行
    public void schedule() throws SchedulerException
    {
        scheduleJobs.startSchedule();
    }
}

 

参考资料

http://www.quartz-scheduler.org

https://gitbook.cn/gitchat/column/5b86228ce15aa17d68b5b55a/topic/5c1f7ad41e59245d4d2b9109

 

Bootstrap Thumbnail Second
MySQL

MySQL is the world's most popular open source database.

GO

Bootstrap Thumbnail Third
算法基础

本书介绍了什么是计算机算法,如何描述它们,以及如何来评估它们。

GO