Spring Boot 整合定时任务完成 从0 到1

news2024/11/17 22:50:05

Java 定时任务学习

定时任务概述

你知道定时任务是如何调度的吗  知乎>

定时任务的应用场景非常广泛, 如果说 我们想要在某时某地去尝试的做某件事 就需要用到定时任务来通知我们 ,大家可以看下面例子

如果需要明天 早起,哪我们一般会去定一个闹钟去通知我们, 而在编程中 有许许多多的场景就需要用到类似的功能 ,例如我们的花呗借款之后,她需要提示你一个还款日期 、又或者 当我们购物下单之后 它需要会在一定的时间内去通知 你支付, 这些个 需要用到 具体 时间通知倒计时的 场景

在 Java中 ,我们如果想实现最简单的定时任务,我们正常的思路应该是这样的,去创建一个线程 ,举例我们借款后,需要在30秒内还款,我们调用了下面的方法,当然只是为了举例,业务场景并不严谨,只是为了演示 ,我们之后了他会停顿30 秒 ,等待你下单完毕

而你如果点击取消借款, 哪我们就使用Thread.stop() 关闭掉了 线程

// 下单后调用还花呗 方法

@Test
  public void testFlowers() {  
        // 单位: 毫秒 通知还花呗
        final long timeInterval = 30000
0;  
        Runnable runnable = new Runnable() {  
            public void run() {  
                while (true) {  
                    // ------- code for task to run  
                    System.out.println("业务还款逻辑 伪代码!");  
                    // ------- ends here  
                    try {  
                        Thread.sleep(timeInterval);  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        };  
        Thread thread = new Thread(runnable);  
        thread.start();  
    }

我们可以看到, 其实类似的场景还有非常多非常多,所以,正常思路我们可能会封装一个工具类,但是业务场景还会有非常多的安全问题, 例如 事务、保证数据的一致性等等、这都是我们考虑的范围,所以我们就需要一个 更完美的方案去解决这类统一的问题,于是定时任务的各种方案就腾空出世 ,相对而言 ,java.util 包 就存在一个解决这个类问题的原生类

在这里插入图片描述

java.util.Timer java的api Timer来完成一个定时任务

这个类出现的非常早,它也是最早专门用来解决这类问题的,我们可以来看一下是怎么使用的 ,通过Timer 对象传入了 一个任务进行触发定时任务

线程安全, 但只会单线程执行, 如果执行时间过长, 就错过下次任务了, 抛出异常时, timerWork会终止

@SpringBootTest
class SchduleTestApplicationTests {

    @Test
    void contextLoads() {
        // 3. 创建一个定时任务
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("每三秒执行一次");
            }

        };
    //1. 创建一个Timer 对象 
        Timer timer = new Timer();
    // 2. 设置定时执行的任务 : schedule(TimerTask task, long delay, long period) 
        timer.schedule(timerTask, 10, 3000);

    }

}

通过上面的方法我们可以很正常的去使用 定时任务 ,解释一下这个里面的含义,首先task这个就是你要定时的一个任务,delay代表延迟多久执行,我们这里为了测试设置为0就就是不延迟,period就是每个多久执行一次,我们为了看到效果设置为3000,也就是3秒执行一次 延迟一毫秒的意思 我们可以从下面看到运行的结果
在这里插入图片描述

在了解了Timer后,我们知道它有些不足,是单线程去运行的,所以JDK又推出了一种结合线程池的定时任务类.即ScheduledExecutorService,针对每个线程任务,会指定线程池中的一个线程去执行.是Timer的更好的一个替代品.
在这里插入图片描述

当然我们也可以使用 线程池的方式去完成一样的结果 ,只是方式不同 ,大家参考学习即可,ScheduledExecutorService 内涵盖了 所有 Timer的功能

public class MyScheduledExecutor {    
    public static void main(String[] args){        
        // 创建任务队列   10 为线程数量      
        ScheduledExecutorService scheduledExecutorService = 
                Executors.newScheduledThreadPool(10); 
        // 执行任务      
        scheduledExecutorService.scheduleAtFixedRate(() -> {          
            System.out.println("打印当前时间:" + new Date());      
        }, 1, 3, TimeUnit.SECONDS); // 1s 后开始执行,每 3s 执行一次   
 
  }
}

当程序初始化完成Timer后,定时任务就会按照我们设定的时间去执行,Timer提供了schedule方法,该方法有多中重载方式来适应不同的情况,如下:

  • schedule(TimerTask task, Date time) :在指定的时间内执行指定的任务。

    schedule(TimerTask task, Date firstTime, long period) :指定的任务在指定的时间开始进行重复的固定延迟执行。

    • schedule(TimerTask task, long delay) :在指定的延迟时间后执行指定的任务。

    • schedule(TimerTask task, long delay, long period) :指定的任务从指定的延迟时间后开始进行重复固定的执行。

      • scheduleAtFixedRate(TimerTask task, Date firstTime, long period) :指定的任务在指定的时间内开始进行重复固定速率的执行。

      • scheduleAtFixedRate(TimerTask task, long delay, long period) :指定的任务在指定的延迟时间后开始进行重复固定速率的执行。

ScheduledExecutorService 优缺点分析 :

优点是,该类是JDK1.5自带的类,使用简单,缺点是该方案仅适用于单机环境。

我们可以 来过一下它的方法

  1. : scheduleAtFixedRate

以给定的间隔去执行任务,当任务的执行时间过长,超过间隔时间后,下一次定时任务的执行会顺延至当前定时任务执行完毕之后.否则严格按照间隔时间去执行.

  */
    public class Task2 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
         //创建一个线程池,以JVM可以利用的CPU数为核心线程数,并指定拒绝策略
                ScheduledThreadPoolExecutor scheduledExecutor = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
                        new RejectedExecutionHandler() {
                            @Override
                            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                System.out.println("当前任务执行失败" + r);
                            }
                        });

                // 在指定0秒延迟后执行,之后每两秒执行一次,此时任务指定时间是大于间隔时间的
                scheduledExecutor.scheduleAtFixedRate(new ThreadRunnable(), 0, 2, TimeUnit.SECONDS);
        }
    }

    class ThreadRunnable implements Runnable{

        @Override
        public void run() {
            //设定执行时间>间隔时间
            System.out.println("线程:"+Thread.currentThread().getName()+",执行任务时间:"+ LocalDateTime.now());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


  1. scheduleWithFixedDelay

以给定的间隔去执行任务,当任务的执行时间过长,超过间隔时间后,下一次定时任务的执行会顺延至当前定时任务执行完毕之后.否则严格按照间隔时间去执行.

 public class Task3 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建一个线程池,以JVM可以利用的CPU数为核心线程数,并指定拒绝策略
            ScheduledThreadPoolExecutor scheduledExecutor = new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("当前任务执行失败" + r);
                    }
                });

            // 在指定1秒延迟后执行,之后每两秒执行一次,此时任务指定时间是大于间隔时间的
            scheduledExecutor.scheduleWithFixedDelay(new ThreadRunnable(), 0, 2, TimeUnit.SECONDS);
        }
    }

    class ThreadRunnable implements Runnable{

        @Override
        public void run() {
            //设定执行时间>间隔时间
            System.out.println("线程:"+Thread.currentThread().getName()+",执行任务时间:"+ LocalDateTime.now());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


我们通过上面两种方式实现了定时任务,接下来来讲讲我们最常用的 一种方式

  1. 导入依赖
 <dependencies>
 
 <!--         下一节需要用到的定时任务依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
 
<!--         Spring 官方自带了依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

从上面的方式当中,我们导入 了 下节的依赖和 Spring 官方自带的Task

Spring Task

@EnableScheduling : 我们通过 这个注解开启 定时任务

@EnableAsync : 开启 异步任务多线程执行

# 配置springtask 定时任务相关的配置

spring:
  task:
    scheduling:
      pool:
        size: 10
      thread-name-prefix: hanhan
    execution:
      shutdown:
#        线程关闭时是否等所有任务结束
        await-termination: false
#        线程最大的等待时间,防止线程陷入循环关不掉
        await-termination-period: 10s


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *  全栈小刘
 */
@EnableScheduling
@EnableAsync
@SpringBootApplication
public class SchduleTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SchduleTestApplication.class, args);
    }


    /**
     * 半小时执行一次
     */
    @Async
    @Scheduled(fixedRate = 30 * 60 * 1000)
    public void plySth() {
        System.out.println(Thread.currentThread().getName() + "当前线程: \t");
        System.out.println("我是半小时执行一次");
    }

    /**
     * 30 秒
     */

    @Scheduled(fixedRate = 30 * 1000)
    public void plySyHello() {
        System.out.println(Thread.currentThread().getName() + "当前线程: \t");
        System.out.println("我是30秒执行一次");
    }

    /**
     * 2 秒
     */
    @Async
    @Scheduled(cron = "0/2 * * * * ? ")
    public void plySyCron() {
        System.out.println(Thread.currentThread().getName() + "当前线程: \t");
        System.out.println("我是2秒执行一次");
    }

    /**
     * 30 秒
     */
    @Scheduled(cron = "30 * * * * ? ")
    public void plySyCron43() {

        System.out.println("我是30秒执行一次");
    }


    @Bean
    public static void setSchdule() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("setSchdule ---> 每三秒执行一次");
            }
        };

        Timer timer = new Timer();

        timer.schedule(timerTask, 10, 3000);
    }

//
//    @Bean
//    public static void setSchdule2() {
//        // 创建一个定时任务
//        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
//        service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
//    }


}

同时大家可以上面这边 也把 我们原先使用的线程池的方式定时任务 也使用进去 了

只需要将它注入Springboot ,这样 就可以正常使用了

在上面的每一个 被 @Scheduled(fixedRate = 30 * 60 * 1000) 所以修饰的方法都是一条异步任务 ,而当你在方法上加入了 @Async 注解 就说明了 这个方法是支持多线程执行的

    @Async
    @Scheduled(cron = "0/2 * * * * ? ")
    public void plySyCron() {
        System.out.println(Thread.currentThread().getName() + "当前线程: \t");
        System.out.println("我是2秒执行一次");
    }

我们注意到了其中有个属性叫做 cron ,这个cron代表的就是 我们可以定时去执行 ,我们可以大概看一下下面的参考,快速学习一下

1

时间表达式

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。
按顺序依次为
秒(0~59)
分钟(0~59)
小时(0~23)
天(月)(0~31,但是你需要考虑你月的天数)
月(0~11)
天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)
年份(1970-2099)


SpringBoot2x系列教程87SpringBoot中整合定时任务字段

常用的表达式为:

0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
“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期间的每1分钟触发
“0 0/5 14 * * ?” 在每天下午2点到下午2:55期间的每5分钟触发
“0 0/5 14,18 * * ?” 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
“0 0-5 14 * * ?” 在每天下午2点到下午2:05期间的每1分钟触发
“0 10,44 14 ? 3 WED” 每年三月的星期三的下午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” 每月的第三个星期五上午10:15触发

大家只需要把con= 换成上面的表达式就好了 ,如果费脑子的花

  @Scheduled(cron = "0/2 * * * * ? ")

当然如果 你需要特别定制化的 cron 表达式 ,大家可以通过下面的 网站进行生成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z99XnSZU-1676120936929)(file://C:\Users\SpiritMark\AppData\Roaming\marktext\images\2023-02-11-19-37-50-image.png)]

cron 网址整理:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IW8FFcsX-1676120936929)(file://C:\Users\SpiritMark\AppData\Roaming\marktext\images\2023-02-11-19-38-25-image.png)]

  • https://cron.qqe2.com/

  • https://www.pppet.net/

  • Cron表达式生成器_quartz、Cron等在线生成执行时间

定时任务整合Springboot

查看源图像

下面这节我们来学习一下如何使用 Springboot 来完成定时任务的整合

在springboot中整合这个技术,我们首先来学习几个词

  • 工作(job):用于执行具体的任务

  • 工作明细(jobDetail):用于定义定时工作的信息

  • 触发器(Trigger):用于描述触发工作的规则,和定义调度通常用Cron来进行定义调度的规则

  • 调度(scheduler):用于描述工作明细和触发器之间的

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合,也可以单独使用。完全由Java开发,可以用来执行定时任务,类似于java.util.Timer 。但是相较于Timer, Quartz增加了很多功能:

  • 持久性作业 - 就是保持调度定时的状态;
  • 作业管理 - 对调度作业进行有效的管理;

Quartz是开源且具有丰富特性的“任务调度库”,能够集成于任何的Java应用,小到独立的应用,大至电子商业系统。Quartz能够创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务。任务job被定义为标准的Java组件,能够执行任何你想要实现的功能。Quartz调度框架包含许多企业级的特性,如JTA事务、集群的支持。

简而言之,Quartz就是基于Java实现的任务调度框架,用于执行你想要执行的任何任务。

  • 官方网址:www.quartz-scheduler.org/

  • 官方文档:www.quartz-scheduler.org/documenta

在上面引入了一个 spring-boot-starter-quartz 的包, 当然我们也直接把原生的引入进行一样可以去使用

<dependencies>
	<!-- Quartz 核心包-->
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
    </dependency>

	<!-- Quartz 工具包 -->
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz-jobs</artifactId>
    </dependency>
</dependencies>

首先我们需要去创建一个配置类 ,专门用来配置我们的 Quartz

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

/**
 * 创建一个定时器
 */
public class TestQuartz extends QuartzJobBean {


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//        System.out.println(context.getJobRunTime());
//        System.out.println("这是我们定时任务、、、、、、");
    }
}

创建好这个 配置类之后,我们可以看到有个 JobExecutionContext 的类, 通过这个类,我们可以用来执行定时任务 ,接下来,我们需要让配置类去加载它

编写一个具体的 任务详情 ,然后通过构造器的构建者模式 去 将他加载进去

SimpleScheduleBuilder 通过这个类我们可以去进行设置 我们的 定时的周期

// 全栈小刘
@Configuration
public class QuartzConfig {


    @Bean
    public JobDetail teatQuartzDetail() {
        return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();
    }

    @Bean
    public Trigger testQuartzTrigger() {
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(10)  //设置时间周期单位秒
                .repeatForever();
        return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())
                .withIdentity("testQuartz")
                .withSchedule(scheduleBuilder)
                .build();

    }

}

接下来我们启动 Springboot ,可以从这里看到 每隔一段时间启动输出一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o8Nh4Rqw-1676120936930)(file://C:\Users\SpiritMark\AppData\Roaming\marktext\images\2023-02-11-20-26-38-image.png)]

当然我们 也可以用这种方式 定一个类

public class Myquartz extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("我们是定时任务");
    }
}

再次配置 ,这里重复的配置只是想告诉大家 ,我们实现 定时任务的方式有很多不同的方式例如下面这种

@Configuration
public class QuartzConfig {
@Bean
    public JobDetail jobDetail(){
        //在这里工作明细要绑定一个job
        //现在这个工作就完成了这里必须要加上storeDurably,这个作用是当这个任务没有被执行也会一直存在保持持久化

    return JobBuilder.newJob(Myquartz.class).storeDurably().build();

    }
    @Bean
    public Trigger trigger(){

    //这里触发器要绑定一个工作明细JobDetail 同时要完成调度
       //这里要说明一下withSchedule是完成调度的下面这行代码是实现时间调度的要
//要说明一下0 0 0 0 0 0,分别代表 秒 分 时 日 月 星期 其中日 和星期会有冲突通常我们都只配一个 另一个用?代替
        //ScheduleBuilder<? extends Trigger> schdule=CronScheduleBuilder.cronSchedule("0 0 0 0 0 0") ;
        //如CronScheduleBuilder.cronSchedule("0 0 0 1 2 ?")这就代表了2月的第一天0秒0分0时 我们还可以配*代表任意 ;
        //还可以如CronScheduleBuilder.cronSchedule("0/15 * * * * ?")代表没个15秒执行一次
        ScheduleBuilder schdule=CronScheduleBuilder.cronSchedule("0/5 * * * * ?") ;
        //forJob就时绑定工作明细
        return TriggerBuilder.newTrigger().forJob(jobDetail()).withSchedule(schdule).build();
    }


}

我们再来设置定义一个 任务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rA90e7zq-1676120936930)(file://C:\Users\SpiritMark\AppData\Roaming\marktext\images\2023-02-11-20-51-59-image.png)]

// 定义任务类
public class HelloJob implements Job {

   @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        // 输出当前时间
        ystem.out.println(new Date());
    }
}

(2)创建任务调度类HelloSchedulerDemo

通过调度器去加载 我们的任务 ,在通过 触发器去触发

public class HelloSchedulerDemo {

    public static void main(String[] args) throws Exception {
        // 1、调度器(Scheduler),从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 2、任务实例(JobDetail)定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
        JobDetail jobDetail = JobBuilder.newJob() // 加载任务类,与HelloJob完成绑定,要求HelloJob实现Job接口
                .withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称
                .build();

        // 3、触发器(Trigger)定义触发器,马上执行,然后每5秒重复执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") // 参数1:触发器的名称(唯一实例);参数2:触发器组的名称
                .startNow() // 马上启动触发器
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()) // 每2秒重复执行一次
                .build();

        // 4、让调度器关联任务和触发器,保证按照触发器定义的调整执行任务
        scheduler.scheduleJob(jobDetail, trigger);

        // 5、启动
        scheduler.start();
        // 关闭
        //scheduler.shutdown();
    }

}

关键信息:

  • Job:工作任务调度的接口,任务需要实现该接口。该接口中定义execute方法,类似JDK提供的TimeTask类的run方法。在里面编写任务执行的业务逻辑。
  • Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法前会创建一个新的 Job 实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。
  • JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDataMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。
  • JobDetail重要属性:namegroupjobClassJobDataMap
JobDetail job = JobBuilder.newJob(HelloJob.class)
        .withIdentity("job1", "group1") // 定义该实例唯一标识,并指定一个组
        .build();

System.out.println("name:" +job.getKey().getName());
System.out.println("group:" +job.getKey().getGroup());
System.out.println("jobClass:" +job.getJobClass().getName());

JobExecutionContext

  • 当 Scheduler 调用一个 Job,就会将 JobExecutionContext 传递给 Job 的 execute() 方法;
  • Job 能通过 JobExecutionContext 对象访问到 Quartz 运行时候的环境以及 Job 本身的明细数据。
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        Trigger trigger = jobExecutionContext.getTrigger(); //获取Trigger
        JobDetail jobDetail = jobExecutionContext.getJobDetail(); //获取JobDetail
        Scheduler scheduler = jobExecutionContext.getScheduler(); //获取Scheduler

        trigger.getKey().getName(); //获取Trigger名字
        trigger.getKey().getGroup(); //获取Trigger组名(默认为 DEFAULT)

        jobExecutionContext.getScheduledFireTime(); //触发器触发的预定时间。
        jobExecutionContext.getFireTime(); //实际触发时间。例如,计划时间可能是 10:00:00,但如果调度程序太忙,实际触发时间可能是 10:00:03。
        jobExecutionContext.getPreviousFireTime(); //上次触发时间
        jobExecutionContext.getNextFireTime(); //下次触发时间

        System.out.println(new Date());
    }
}

JobDataMap

  • 使用 Map进行获取

  • 在进行任务调度时,JobDataMap 存储在 JobExecutionContext 中,非常方便获取。

  • JobDataMap 可以用来装载任何可序列化的数据对象,当 Job 实例对象被执行时这些参数对象会传递给它。

  • JobDataMap 实现了 JDK 的 Map 接口,并且添加了非常方便的方法用来存取基本数据类型。

在定义 Trigger 或者 JobDetail 时,将 JobDataMap 传入,然后 Job 中便可以获取到 JobDataMap 中的参数

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        //1. 调度器(Scheduler)
        Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler();
		
		JobDataMap jobDataMap2 = new JobDataMap();
        jobDataMap2.put("message", "JobDetailMessage");

        //2. 任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "jobGroup1")
                .usingJobData(jobDataMap2)
                .build();

		// 定义 JobDataMap
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("message", "TriggerMessage");

        //3. 触发器(Trigger)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "triggerGroup1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
                .endAt(new Date(new Date().getTime() + 3000L))
                .usingJobData(jobDataMap) // 将 JobDataMap 放入 Trigger 中
                .build();

        defaultScheduler.scheduleJob(jobDetail, trigger);
        defaultScheduler.start();
    }
}

HelloJob.java

public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) hrows JobExecutionException {
    	System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("message")); //TriggerMessage
        System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("message")); //JobDetailMessage

        System.out.println(jobExecutionContext.getMergedJobDataMap().get("message")); //TriggerMessage
        System.out.println(new Date());
    }
}

使用 Setter 方法获取

Job实现类中添加setter方法对应JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法。

HelloScheduler 类和上面一样。

@Data
public class HelloJob implements Job {

    private String mesage;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(message); //TriggerMessage
        System.out.println(new Date());
    }
}

*注意:如果遇到同名的 keyTriggerJobDataMap 的值会覆盖 JobDetailJobDataMap 同名的 Key

更多具体的细节 大家可以关注 :

使用 Quartz_w3cschool

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/338825.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ssm高校功能教室预约系统java idea maven

本网站所实现的是一个高校功能教室预约系统&#xff0c;该系统严格按照需求分析制作相关模块&#xff0c;并利用所学知识尽力完成&#xff0c;但是本人由于学识浅薄&#xff0c;无法真正做到让该程序可以投入市场使用&#xff0c;仅仅简单实现部分功能&#xff0c;希望日后还能…

springboot集成Redis

springboot集成Redis1 windows平台安装Redis2 引入依赖3 修改配置文件4 启动类添加注解5 指定缓存哪个方法6 配置Redis的超时时间小BUG测试对于项目中一些访问量较大的接口&#xff0c;配置上Redis缓存&#xff0c;提升系统运行速度。1 windows平台安装Redis github.com/Micro…

谈一谈API接口开发

做过开发的程序猿&#xff0c;基本都写过接口&#xff0c;写接口不算难事&#xff0c;与接口交互的对象核对好接口的地址、请求参数和响应参数即可&#xff0c;我在作为面试官去面试开发人员的时候&#xff0c;有时候会问这个问题&#xff0c;但相当多的一部分人并没有深入的考…

BERT(NAACL 2019)-NLP预训练大模型论文解读

文章目录摘要算法BERT预训练Masked LMNSPFine-tune BERT实验GLUESQuAD v1.1SQuAD v2.0SWAG消融实验预训练任务影响模型大小影响BERT基于特征的方法结论论文&#xff1a; 《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》github&#xff…

QT+OpenGL 摄像机

QTOpenGL 摄像机 本篇完整工程见gitee:QtOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主 OpenGL本身没有摄像机的定义&#xff0c;但是我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机&#xff0c;产生一…

Linux内核启动(2,0.11版本)内核启动前的苦力活与内核启动

内核启动前的工作 在上一章的内容中&#xff0c;我们跳转到了setup.s的代码部分&#xff0c;这章我们先讲一讲setup做了什么吧 entry start start:! ok, the read went well so we get current cursor position and save it for ! posterity.mov ax,#INITSEG ! this is done …

Flowable进阶学习(十)定时器、ServiceTask服务任务、ScriptTask脚本任务

文章目录一、定时器1. 流程定义定时激活2. 流程实例定时挂起3. 定时任务执行过程ServiceTask 服务任务委托表达式表达式类中字段ScriptTask 脚本任务JS TASK一、定时器 相关知识链接阅读&#xff1a;事件网关——定时器启动事件 1. 流程定义定时激活 可以通过activateProces…

材质笔记 - Simluate Solid Surface

光的行为 当光和物体相遇时&#xff0c;光会有三种行为&#xff1a;被物体反射、穿过物体&#xff08;物体是透明或半透明的&#xff09;或者被吸收。 高光反射和漫反射 高光反射&#xff08;Specular Reflection&#xff09;会在表面光滑且反光的物体上看到&#xff0c;比如镜…

SMART PLC时间间隔定时器应用(高速脉冲测频/测速)

高速脉冲计数测量频率,专栏有系列文章分析讲解,这里不再赘述(原理都是利用差分代替微分)。具体链接如下: 西门子SMART PLC高速脉冲计数采集编码器速度(RC滤波)_RXXW_Dor的博客-CSDN博客这篇文章主要讲解西门子 SMART PLC高速计数采集编码器脉冲信号计算速度,根据编码器脉…

鸢尾花数据集分类(PyTorch实现)

一、数据集介绍 Data Set Information: This is perhaps the best known database to be found in the pattern recognition literature. Fisher’s paper is a classic in the field and is referenced frequently to this day. (See Duda & Hart, for example.) The data…

[Android Studio]Android 数据存储-文件存储学习笔记-结合保存QQ账户与密码存储到指定文件中的演练

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…

戴尔游匣G16电脑U盘安装系统操作教程分享

戴尔游匣G16电脑U盘安装系统操作教程分享。有用户在使用戴尔游匣G16电脑的时候遇到了系统问题&#xff0c;比如电脑蓝屏、自动关机重启、驱动不兼容等问题。遇到这些问题如果无法进行彻底解决&#xff0c;我们可以通过U盘重新安装系统的方法来解决&#xff0c;因为这些问题一般…

I.MX6ULL内核开发7:led字符设备驱动实验

目录 一、led字符设备驱动实验 二、驱动模块初始化 三、虚拟地址读写 四、自定义led的file_operation接口 五、拷贝数据 六、register_chrdev函数 七、 __register_chrdev函数 八、编译执行 一、led字符设备驱动实验 驱动模块内核模块(.ko)驱动接口(file_operations) …

Mysql 增删改查(一) —— 查询(条件查询where、分页limits、排序order by)

查询 select 可以认为是四个基本操作中使用最为频繁的操作&#xff0c;然而数据量比较大的时候&#xff0c;我们不可能查询所有内容&#xff0c;我们一般会搭配其他语句进行查询&#xff1a; 假如要查询某一个字段的内容&#xff0c;可以使用 where假如要查询前几条记录&#…

STM32----搭建Arduino开发环境

搭建Arduino开发环境前言一、Arduino软件1.软件下载2.软件安装3.软件操作二、Cortex官方内核三、烧录下载四、其他第三方内核1.Libmaple内核2.Steve改进的LibMaple 内核3.STMicroelectronics(ST)公司编写的内核总结前言 本章介绍搭建STM32搭建Arduino开发环境&#xff0c;包括…

leetcode470 用Rand7()实现Rand10()

力扣470 第一步&#xff1a;根据Rand7()函数制作一个可以随机等概率生成0和1的函数rand_0and1 调用Rand7()函数&#xff0c;随机等概率生成1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7 这时我们设置&#xff1a;生成1&#xff0c;2&a…

“深度学习”学习日记。卷积神经网络--用CNN的实现MINIST识别任务

2023.2.11 通过已经实现的卷积层和池化层&#xff0c;搭建CNN去实现MNIST数据集的识别任务&#xff1b; 一&#xff0c;简单CNN的网络构成&#xff1a; 代码需要在有网络的情况下运行&#xff0c;因为会下载MINIST数据集&#xff0c;运行后会生成params.pkl保留训练权重&…

【吉先生的Java全栈之路】

吉士先生Java全栈学习路线&#x1f9e1;第一阶段Java基础: 在第一阶段:我们要认真听讲,因为基础很重要!基础很重要!基础很重要!!! 重要的事情说三遍。在这里我们先学JavaSE路线&#xff1b;学完之后我们要去学第一个可视化组件编程《GUI》&#xff1b;然后写个《贪吃蛇》游戏耍…

微搭低代码从入门到精通05-变量定义

我们上一篇对应用编辑器有了一个整体的介绍。要想零基础开发小程序&#xff0c;就得从各种概念开始学起。 如果你是零基础学习开发&#xff0c;无论学习哪一门语言&#xff0c;第一个需要掌握的知识点就是变量。 那么什么是变量&#xff1f;变量其实就是存放数据的一个容器&a…

专题 | 防抖和节流

一 防抖&#xff1a;单位时间内&#xff0c;频繁触发事件&#xff0c;只执行最后一次 场景&#xff1a;搜索框搜索输入&#xff08;利用定时器&#xff0c;每次触发先清掉以前的定时器&#xff0c;从新开始&#xff09; 节流&#xff1a;单位时间内&#xff0c;频繁触发事件&…