当前位置: 首页 > news >正文

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

Java 定时任务学习

定时任务概述

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

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

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

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

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

// 下单后调用还花呗 方法@Testpublic 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 {@Testvoid contextLoads() {// 3. 创建一个定时任务TimerTask timerTask = new TimerTask() {@Overridepublic 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() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("当前任务执行失败" + r);}});// 在指定0秒延迟后执行,之后每两秒执行一次,此时任务指定时间是大于间隔时间的scheduledExecutor.scheduleAtFixedRate(new ThreadRunnable(), 0, 2, TimeUnit.SECONDS);}}class ThreadRunnable implements Runnable{@Overridepublic 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() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("当前任务执行失败" + r);}});// 在指定1秒延迟后执行,之后每两秒执行一次,此时任务指定时间是大于间隔时间的scheduledExecutor.scheduleWithFixedDelay(new ThreadRunnable(), 0, 2, TimeUnit.SECONDS);}}class ThreadRunnable implements Runnable{@Overridepublic 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: 10thread-name-prefix: hanhanexecution: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秒执行一次");}@Beanpublic static void setSchdule() {TimerTask timerTask = new TimerTask() {@Overridepublic 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 {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {
//        System.out.println(context.getJobRunTime());
//        System.out.println("这是我们定时任务、、、、、、");}
}

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

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

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

// 全栈小刘
@Configuration
public class QuartzConfig {@Beanpublic JobDetail teatQuartzDetail() {return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();}@Beanpublic 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 {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {System.out.println("我们是定时任务");}
}

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

@Configuration
public class QuartzConfig {
@Beanpublic JobDetail jobDetail(){//在这里工作明细要绑定一个job//现在这个工作就完成了这里必须要加上storeDurably,这个作用是当这个任务没有被执行也会一直存在保持持久化return JobBuilder.newJob(Myquartz.class).storeDurably().build();}@Beanpublic 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 {@Overridepublic 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 {@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {Trigger trigger = jobExecutionContext.getTrigger(); //获取TriggerJobDetail jobDetail = jobExecutionContext.getJobDetail(); //获取JobDetailScheduler scheduler = jobExecutionContext.getScheduler(); //获取Schedulertrigger.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();// 定义 JobDataMapJobDataMap 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 {@Overridepublic void execute(JobExecutionContext jobExecutionContext) hrows JobExecutionException {System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("message")); //TriggerMessageSystem.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("message")); //JobDetailMessageSystem.out.println(jobExecutionContext.getMergedJobDataMap().get("message")); //TriggerMessageSystem.out.println(new Date());}
}

使用 Setter 方法获取

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

HelloScheduler 类和上面一样。

@Data
public class HelloJob implements Job {private String mesage;@Overridepublic void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {System.out.println(message); //TriggerMessageSystem.out.println(new Date());}
}

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

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

使用 Quartz_w3cschool

相关文章:

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

Java 定时任务学习 定时任务概述 > 定时任务的应用场景非常广泛, 如果说 我们想要在某时某地去尝试的做某件事 就需要用到定时任务来通知我们 &#xff0c;大家可以看下面例子 如果需要明天 早起&#xff0c;哪我们一般会去定一个闹钟去通知我们, 而在编程中 有许许多多的…...

Dialogue Transformers

Abstract 本文介绍了一种基于 Transformer 架构的 对话策略,其中自注意力机制被应用于对话轮次(dialogue turns)的序列上。近期的一些工作使用层次化的循环神经网络(hierarchical recurrent neural networks)在对话上下文中对多个话语(utterances)进行编码,但是我们认…...

【遇见青山】项目难点:缓存击穿问题解决方案

【遇见青山】项目难点&#xff1a;缓存击穿问题解决方案1.缓存击穿互斥锁&#x1f512;方案逻辑过期方案2.基于互斥锁方案的具体实现3.基于逻辑过期方案的具体实现1.缓存击穿 缓存击穿问题也叫热点Key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的key突然失效…...

2023Flag具体实施计划(短期)

重新看了flag ,要做的事情太多&#xff0c;太杂&#xff0c;上周一周时间都在纠结和琢磨&#xff0c;该怎么下手。如何达成小目标。特别是沟通&#xff0c;汇报&#xff0c;演讲能力&#xff0c; 以及整体体系化的思维能力的训练。如何做到多思考&#xff0c;而不是瞎搞。这边重…...

研一寒假C++复习笔记--左值和右值的理解和使用

目录 1--左值和右值的定义 2--简单理解左值和右值的代码 3--非const引用只能接受左值 1--左值和右值的定义 左值&#xff1a;L-Value&#xff0c;L理解为 Location&#xff0c;表示可寻&#xff1b; 右值&#xff1a;R-Value&#xff0c;R理解为 Read&#xff0c;表示可读&a…...

Android 11.0 动态修改SystemProperties中ro开头系统属性的值

需求&#xff1a; 在11.0的产品开发中&#xff0c;对于定制功能的需求很多&#xff0c;有些机型要求可以修改系统属性值&#xff0c;对于系统本身在10.0以后为了系统安全性&#xff0c;不允许修改ro开头的SystemProperties的值&#xff0c;所以如果要求修改ro的相关系统属性&am…...

为什么分库分表

系列文章目录 文章目录系列文章目录前言一、什么是分库分表二、分库分表的原因分库分表三、如何分库分表3.1 垂直拆分1.垂直分库2、垂直分表3.2 水平拆分水平分库水平分表水平分库分表的策略hash取模算法range范围rangehash取模混合地理位置分片预定义算法四、分库分表的问题分…...

1625_MIT 6.828 stabs文档信息整理_下

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 继续之前的学习笔记&#xff0c;整理一下最近看过的一点stabs资料。 这一页中有一半的信息是Fortran专用的&#xff0c;直接跳过。参数的符号修饰符是p&#xff0c…...

论文阅读 | Rethinking Coarse-to-Fine Approach in Single Image Deblurring

前言&#xff1a;ICCV2021图像单帧运动去糊论文 论文地址&#xff1a;【here】 代码地址&#xff1a;【here】 Rethinking Coarse-to-Fine Approach in Single Image Deblurring 引言 图像去糊来自与物体或相机的运动。现有的deblur领域的深度学习方法大多都是coarse-to-fin…...

Mysql 增删改查(二)—— 增(insert)、删(delete)、改(update)

目录 一、插入 1、insert 2、replace&#xff08;插入否则更新&#xff09; 二、更新&#xff08;update&#xff09; 三、删除 1、delete 2、truncate&#xff08;截断表&#xff0c;慎用&#xff09; 一、插入 1、insert (1) 单行 / 多行插入 全列插入&#xff1a;…...

JSD2212复习串讲

1. Java语言基础阶段 这一部分主要是练&#xff0c;给一些题目还有讲解一些最基础的语法&#xff0c;做一些额外的补充 1.1 基本概念 1.2 变量 1.2.1 数据类型 4类8种 基本类型&#xff1a;整形、浮点型、字符型、布尔型 整形&#xff1a;byte -》short-》int-》long 浮点…...

sphinx 升级到6.x后的Jquery问题

sphinx 升级到6.0 后&#xff0c;以前对于jquery的默认引用方式发生了改变以前在编译后的html中jquery是如下引用的&#xff1a;<script src"_static/jquery.js"></script>而升级到6.0后&#xff0c;对于jquery 是一个googleapi的远程jquery调用&#xf…...

NSSCTF Round#8 Basic

from:http://v2ish1yan.top MyDoor 使用php伪协议读取index.php的代码 php://filter/readconvert.base64-encode/resourceindex.php<?php error_reporting(0);if (isset($_GET[N_S.S])) {eval($_GET[N_S.S]); }if(!isset($_GET[file])) {header(Location:/index.php?fi…...

多传感器融合定位十二-基于图优化的建图方法其一

多传感器融合定位十二-基于图优化的建图方法其一1. 基于预积分的融合方案流程1.1 优化问题分析1.2 预积分的作用1.3 基于预积分的建图方案流程2. 预积分模型设计3. 预积分在优化中的使用3.1 使用方法3.2 残差设计3.3 残差雅可比的推导3.3.1 姿态残差的雅可比3.3.2 速度残差的雅…...

RockChip MPP编码

概述瑞芯微提供的媒体处理软件平台&#xff08;Media Process Platform&#xff0c;简称 MPP&#xff09;是适用于瑞芯微芯片系列的通用媒体处理软件平台。该平台对应用软件屏蔽了芯片相关的复杂底层处理&#xff0c;其目的是为了屏蔽不同芯片的差异&#xff0c;为使用者提供统…...

【学习笔记】NOIP暴零赛2

细思极恐&#xff0c;我的能力已经退步到这个地步了吗&#xff1f; 数据结构 这题的修改是强行加进去迷惑你的。 考虑怎么求树的带权重心。 完了我只会树形dp 完了完了 结论&#xff1a;设uuu的子树和为szusz_uszu​&#xff0c;所有点权值和为sss&#xff0c;那么树的带…...

linux基本功系列之hostname实战

文章目录前言一. hostname命令介绍二. 语法格式及常用选项三. 参考案例3.1 显示本机的主机名3.2 临时修改主机名3.3 显示短格式的主机名3.4 显示主机的ip地址四. 永久修改主机名4.1 centos6 修改主机名的方式4.2 centos7中修改主机名永久生效总结前言 大家好&#xff0c;又见面…...

Easy-Es框架实践测试整理 基于ElasticSearch的ORM框架

文章目录介绍&#xff08;1&#xff09;Elasticsearch java 客户端种类&#xff08;2&#xff09;优势和特性分析&#xff08;3&#xff09;性能、安全、拓展、社区&#xff08;2&#xff09;ES版本及SpringBoot版本说明索引处理&#xff08;一&#xff09;索引别名策略&#x…...

【数据结构】双向链表的模拟实现(无头)

目录 前言&#xff1a; 1、认识双向链表中的结点 2、认识并创建无头双向链表 3、实现双向链表当中的一些方法 3.1、遍历输出方法&#xff08;display&#xff09; 3.2、得到链表的长度&#xff08;size&#xff09; 3.3、查找关键字key是否包含在双链表中(contains) 3.…...

vue自定义指令---处理加载图片失败时出现的碎图,onerror事件

目录 一、自定义指令 1、局部注册和使用 2、全局注册和使用 二、自定义指令处理图片加载失败&#xff08;碎图&#xff09; 一、自定义指令 vue中除v-model、v-show等内置指令之外&#xff0c;还允许注册自定义指令&#xff0c;获取DOM元素&#xff0c;扩展额外的功能。 1、局…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...