SpringBoot 整合 Quartz
一:Quartz 百度百科
quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。
调度简单
Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!简单地创建一个实现org.quartz.Job接口的java类。Job接口包含唯一的方法:
public void execute(JobExecutionContext context)
throws JobExecutionException;
在你的Job接口实现类里面,添加一些逻辑到execute()方法。一旦你配置好Job实现类并设定好调度时间表,Quartz将密切注意剩余时间。当调度程序确定该是通知你的作业的时候,Quartz框架将调用你Job实现类(作业类)上的execute()方法并允许做它该做的事情。无需报告任何东西给调度器或调用任何特定的东西。仅仅执行任务和结束任务即可。如果配置你的作业在随后再次被调用,Quartz框架将在恰当的时间再次调用它。
内部架构
在规模方面,Quartz跟大多数开源框架类似。大约有300个java类和接口,并被组织到12个包中。这可以和Apache Struts把大约325个类和接口以及组织到11个包中相比。尽管规模几乎不会用来作为衡量框架质量的一个特性,但这里的关键是quartz内含很多功能,这些功能和特性集是否成为、或者应该成为评判一个开源或非开源框架质量的因素。
调度器
Quartz框架的核心是调度器。调度器负责管理Quartz应用运行时环境。调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件。Quartz不仅仅是线程和线程管理。为确保可伸缩性,Quartz采用了基于多线程的架构。启动时,框架初始化一套worker线程,这套线程被调度器用来执行预定的作业。这就是Quartz怎样能并发运行多个作业的原理。Quartz依赖一套松耦合的线程池管理部件来管理线程环境。本篇文章中,我们会多次提到线程池管理,但Quartz里面的每个对象是可配置的或者是可定制的。所以,例如,如果你想要插进自己线程池管理设施,我猜你一定能!
框架特征
Quartz框架有一个丰富的特征集。事实上,Quartz有太多特性以致不能在一种情况中全部领会,下面列出了一些有意思的特征,但没时间在此详细讨论。
监听器和插件
每个人都喜欢监听和插件。今天,几乎下载任何开源框架,你必定会发现支持这两个概念。监听是你创建的java类,当关键事件发生时会收到框架的回调。例如,当一个作业被调度、没有调度或触发器终止和不再触发时,这些都可以通过设置来通知你的监听器。Quartz框架包含了调度器监听、作业和触发器监听。你可以配置作业和触发器监听为全局监听或者是特定于作业和触发器的监听。
一旦你的一个具体监听被调用,你就能使用这个技术来做一些你想要在监听类里面做的事情。例如,你如果想要在每次作业完成时发送一个电子邮件,你可以将这个逻辑写进作业里面,也可以写进JobListener里面。写进JobListener的方式强制使用松耦合有利于设计上做到更好。
Quartz插件是一个新的功能特性,无须修改Quartz源码便可被创建和添加进Quartz框架。他为想要扩展Quartz框架又没有时间提交改变给Quartz开发团队和等待新版本的开发人员而设计。如果你熟悉Struts插件的话,那么完全可以理解Quartz插件的使用。
与其Quartz提供一个不能满足你需要的有限扩展点,还不如通过使用插件来拥有可修整的扩展点。
集群Quartz应用
Quartz应用能被集群,是水平集群还是垂直集群取决于你自己的需要。集群提供以下好处:
·伸缩性
·高可用性
·负载均衡
Quartz可以借助关系数据库和JDBC作业存储支持集群。
Terracotta扩展quartz提供集群功能而不需要数据库支持
相关工具
Quartz经常会用到cron表达式,可以使用国外网站cronmaker辅助生成cron表达式。
二: Quartz 的使用思路
- Job-任务: 你要做什么事情
- Trigger-触发器:你什么时候去做
- Scheduler-任务调度器:你什么时候需要去做什么事
什么是Quartz?
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
持久性作业 - 就是保持调度定时的状态;
作业管理 - 对调度作业进行有效的管理;
大部分公司都会用到定时任务这个功能。
拿火车票购票来说,当你下单后,后台就会插入一条待支付的task(job),一般是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消此次订单;当你支付完成之后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。在我们实际的项目中,当Job过多的时候,肯定不能人工去操作,这时候就需要一个任务调度框架,帮我们自动去执行这些程序。那么该如何实现这个功能呢?
(1)首先我们需要定义实现一个定时功能的接口,我们可以称之为Task(或Job),如定时发送邮件的task(Job),重启机器的task(Job),优惠券到期发送短信提醒的task(Job),实现接口如下:
(2)有了任务之后,还需要一个能够实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等。
(3)有了Job和Trigger后,怎么样将两者结合起来呢?即怎样指定Trigger去执行指定的Job呢?这时需要一个Schedule,来负责这个功能的实现
上面三个部分就是Quartz的基本组成部分:
- 调度器:Scheduler
- 任务:JobDetail
- 触发器:Trigger,包括SimpleTrigger和CronTrigger
Quartz的三大核心组件
调度器:Scheduler。
任务:JobDetail。
触发器:Trigger,包括 SimpleTrigger 和 CronTrigger。
(1)Job(任务):是一个接口,有一个方法 void execute(JobExecutionContext context) ,可以通过实现该接口来定义需要执行的任务(具体的逻辑代码)。JobDetail:Quartz每次执行Job时,都重新创建一个Job实例,会接收一个Job实现类,以便运行的时候通过newInstance()的反射调用机制去实例化Job。JobDetail是用来描述Job实现类以及相关静态信息,比如任务在scheduler中的组名等信息。
(2)Trigger(触发器):描述触发Job执行的时间触发规则实现类SimpleTrigger和CronTrigger可以通过crom表达式定义出各种复杂的调度方案。
Calendar:是一些日历特定时间的集合。一个Trigger可以和多个 calendar关联,比如每周一早上10:00执行任务,法定假日不执行,则可以通过calendar进行定点排除。
(3)Scheduler(调度器):代表一个Quartz的独立运行容器。Trigger和JobDetail可以注册到Scheduler中。Scheduler可以将Trigger绑定到某一JobDetail上,这样当Trigger被触发时,对应的Job就会执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job
CronTrigger配置格式
格式:[秒] [分] [小时] [日] [月] [周] [年]
序号 说明 是否必填 允许填写的值 允许的通配符
1 秒 是 0-59 , - * /
2 分 是 0-59 , - * /
3 小时 是 0-23 , - * /
4 日 是 1-31 , - * ? / L W
5 月 是 1-12 or JAN-DEC , - * /
6 周 是 1-7 or SUN-SAT , - * ? / L #
7 年 否 empty 或 1970-2099 , - * /通配符说明:
* 表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。
? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
- 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。
, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发
/ 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。
L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-").
小提示:'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资) 。
# 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了)
小提示:周字段的设置,若使用英文字母是不区分大小写的 MON 与mon相同。可通过在线生成Cron表达式的工具:http://cron.qqe2.com/ 来生成自己想要的表达式。
常用示例:
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分每分触发
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)每天下午的 18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发
0 10,44 14 ? 3 WED 3月分每周三下午的 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 每月的第三周的星期五开始触发
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)
Quartz学习资料: 《Quartz官方文档》
三:SpringBoot 整合 Quartz
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.alan</groupId> <artifactId>SpingbootQuartz</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringBootQuartz</name> <description>SpringBootQuartz</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot 整合 Quartz 定时任务 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> <exclusions> <exclusion> <!--去掉自带的日志记录的jar包--> <groupId>slf4j-api</groupId> <artifactId>org.slf4j</artifactId> </exclusion> </exclusions> </dependency> <!-- 添加Scheduled 坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!--Spring tx 坐标 Spring 事务的jar包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
SpringBootQuartzApplication
package com.alan.SpringBootQuartz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication //修改启动类 增加定时任务 注解 @EnableScheduling public class SpringBootQuartzApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzApplication.class, args); } }
SpringBootQuartzApplicationTests
package com.alan.SpringBootQuartz; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class SpringBootQuartzApplicationTests { @Test void contextLoads() { } }
QuartzDemo
package com.alan.SpringBootQuartz.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; /* * Job 类 :定义任务类 * 在 com.alan.SpringBootQuartz.quartz 包下创建同步用户信息Job类(QuartzDemo.java), * 并继承 Job 类, * 重写 public void execute(JobExecutionContext context) throws JobExecutionException 方法。 * @author: Alan_liu * @date 2021/6/5 12:47 */ public class QuartzDemo implements Job { /** * * 任务被触发时被执行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("execute .............>>>>>>.........."+new Date()); } }
QuartzMain
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.SpringBootQuartzApplication; import com.alan.SpringBootQuartz.quartz.QuartzDemo; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; /* * @author: Alan_liu * @date 2021/6/5 12:52 */ public class QuartzMain { public static void main(String[] args) { QuartzSimpleScheduleBuilderTest(); } public static void QuartzSimpleScheduleBuilderTest( ) { try { //1:创建job 对象,你要做什么事情 JobDetail job= JobBuilder.newJob(QuartzDemo.class).build(); //2:创建trigger对象,在什么时间做 /* * 简单的 trigger 触发时间,通过quartz 提供的 一个方法完成简单的重复任务 * cron trigger :按照Cron的表达式来给定触发时间 * */ Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever()).build(); //3:创建Schedule对象:在什么时间做什么事情 Scheduler scheduled= StdSchedulerFactory.getDefaultScheduler(); scheduled.scheduleJob(job,trigger); //启动 scheduled.start(); } catch (Exception e) { e.printStackTrace(); } } public static void QuartzCronScheduleBuilderTest( ) { try { //1:创建job 对象,你要做什么事情 JobDetail job= JobBuilder.newJob(QuartzDemo.class).build(); //2:创建trigger对象,在什么时间做 Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build(); //3:创建Schedule对象:在什么时间做什么事情 Scheduler scheduled= StdSchedulerFactory.getDefaultScheduler(); scheduled.scheduleJob(job,trigger); //启动 scheduled.start(); } catch (Exception e) { e.printStackTrace(); } } }
QuartzConfigDemo
package com.alan.SpringBootQuartz.quartz;/* * Todo * * @author: Alan_liu * @date 2021/6/5 21:26 */ import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; public class QuartzConfigDemo implements Job { /** * * 任务被触发时被执行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("QuartzConfigDemo execute .............>>>>>>.........."+new Date()); } }
QuartzConfig
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.quartz.QuartzConfigDemo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; import javax.xml.crypto.dsig.keyinfo.RetrievalMethod; import java.nio.channels.Pipe; /* * Quartz配置类 * @author: Alan_liu * @date 2021/6/5 21:12 */ @Configuration public class QuartzConfig { /*** * 1:创建job对象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public JobDetailFactoryBean jobDetailFactoryBean(){ JobDetailFactoryBean factory=new JobDetailFactoryBean(); //关联自己创建的job类 factory.setJobClass(QuartzConfigDemo.class); return factory; } /*** * 2: 创建trigger对象 * @user Alan_liu * @date 2021/6/5 21:13 */ /* @Bean public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ SimpleTriggerFactoryBean factory =new SimpleTriggerFactoryBean(); //关联jobDetail对象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //该入参表示一个执行的毫秒数 factory.setRepeatInterval(2000); //设置启动后重复执行次数:5次 factory.setRepeatCount(5); return factory; }*/ /** * * Cron Trigger */ @Bean public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ CronTriggerFactoryBean factory=new CronTriggerFactoryBean(); //关联jobDetail对象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //设置触发时间 factory.setCronExpression("0/2 * * * * ?"); return factory; } /*** * 3:创建Scheduler 对象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public SchedulerFactoryBean schedulerFactoryBean(/*SimpleTriggerFactoryBean simpleTriggerFactoryBean*/CronTriggerFactoryBean cronTriggerFactoryBean ){ SchedulerFactoryBean factory=new SchedulerFactoryBean(); //关联trigger factory.setTriggers(/*simpleTriggerFactoryBean.getObject()*/ cronTriggerFactoryBean.getObject()); return factory; } }
Job 类中注入业务逻辑层对象
Pom.xml
略。。。。。。。。。!
项目启动类:SpringBootQuartzApplication
package com.alan.SpringBootQuartz; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication //修改启动类 增加定时任务 注解 @EnableScheduling public class SpringBootQuartzApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzApplication.class, args); } }
业务逻辑层对象:UsersService
package com.alan.SpringBootQuartz.Service; /* * 业务类 注入到job 类中 的示例 * @author: Alan_liu * @date 2021/6/5 21:45 */ import org.springframework.stereotype.Service; @Service public class UsersService { public void addUsers(){ System.out.println("Add Users..............>>>>>>........"); } }
将业务层对象添加到定时任务对象:QuartzConfigDemo:
package com.alan.SpringBootQuartz.quartz;/* * Todo * * @author: Alan_liu * @date 2021/6/5 21:26 */ import com.alan.SpringBootQuartz.Service.UsersService; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; public class QuartzConfigDemo implements Job { @Autowired private UsersService usersService; /** * * 任务被触发时被执行的方法 * @user Alan_liu * @date 2021/6/5 12:50 */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("QuartzConfigDemo execute .............>>>>>>.........."+new Date()); /** * * 将数据逻辑处理的业务层对象注入到 定时任务器里后。 * 在启动springboot 项目后,进行实例化QuartzConfigDemo对象的时候 * 并没有通过spring ioc 容器进行完成注入 * 解决方案: * 创建MyAdaptableJobFactory 对象 对 AdaptableJobFactory 对象进而对JobFactory对象 * 的createJobInstance 方法进行重写,通过 调用AutowireCapableBeanFactory * 进行手动把 ReflectionUtils.accessibleConstructor(jobClass).newInstance();的对象注入到spring ioc容器中 * * ****/ this.usersService.addUsers(); } }
重新实例化 jobfactory :MyAdaptableJobFactory
重写实例化jobfactory 的业务代码:
package com.alan.SpringBootQuartz.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /* * 用于解决: *org.quartz.SchedulerException: Job threw an unhandled exception. * at org.quartz.core.JobRunShell.run(JobRunShell.java:213) ~[quartz-2.3.2.jar:na] * at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.2.jar:na] *Caused by: java.lang.NullPointerException: null * at com.alan.SpringBootQuartz.quartz.QuartzConfigDemo.execute(QuartzConfigDemo.java:28) ~[classes/:na] * at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.2.jar:na] * ... 1 common frames omitted * * @author: Alan_liu * @date 2021/6/5 21:52 */ //进行实例化 MyAdaptableJobFactory 对象 @Component("myAdaptableJobFactory") public class MyAdaptableJobFactory extends AdaptableJobFactory { /*** *AutowireCapableBeanFactory 可以将一个对象添加到SPringIOC容器中,并且完成该对象的属性注入 */ @Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory; /*** * 该方法需要将实例化的任务对象手动添加到springIOC容器中并且完成对象注入 * @user Alan_liu * @date 2021/6/5 21:55 */ @Override protected Object createJobInstance(TriggerFiredBundle bundle)throws Exception{ Object obj=super.createJobInstance(bundle); //将Obj对象添加到Spring IOC容器中,并完成注入 this.autowireCapableBeanFactory.autowireBean(obj); return obj; } }
框架代码:被用于继承的对象:
/* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.scheduling.quartz; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; import org.springframework.util.ReflectionUtils; /** * {@link JobFactory} implementation that supports {@link java.lang.Runnable} * objects as well as standard Quartz {@link org.quartz.Job} instances. * * <p>Compatible with Quartz 2.1.4 and higher, as of Spring 4.1. * * @author Juergen Hoeller * @since 2.0 * @see DelegatingJob * @see #adaptJob(Object) */ public class AdaptableJobFactory implements JobFactory { @Override public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException { try { Object jobObject = createJobInstance(bundle); return adaptJob(jobObject); } catch (Throwable ex) { throw new SchedulerException("Job instantiation failed", ex); } } /** * Create an instance of the specified job class. * <p>Can be overridden to post-process the job instance. * @param bundle the TriggerFiredBundle from which the JobDetail * and other info relating to the trigger firing can be obtained * @return the job instance * @throws Exception if job instantiation failed */ protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Class<?> jobClass = bundle.getJobDetail().getJobClass(); return ReflectionUtils.accessibleConstructor(jobClass).newInstance(); } /** * Adapt the given job object to the Quartz Job interface. * <p>The default implementation supports straight Quartz Jobs * as well as Runnables, which get wrapped in a DelegatingJob. * @param jobObject the original instance of the specified job class * @return the adapted Quartz Job instance * @throws Exception if the given job could not be adapted * @see DelegatingJob */ protected Job adaptJob(Object jobObject) throws Exception { if (jobObject instanceof Job) { return (Job) jobObject; } else if (jobObject instanceof Runnable) { return new DelegatingJob((Runnable) jobObject); } else { throw new IllegalArgumentException( "Unable to execute job class [" + jobObject.getClass().getName() + "]: only [org.quartz.Job] and [java.lang.Runnable] supported."); } } }
/* * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz.spi; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; /** * <p> * A JobFactory is responsible for producing instances of <code>Job</code> * classes. * </p> * * <p> * This interface may be of use to those wishing to have their application * produce <code>Job</code> instances via some special mechanism, such as to * give the opertunity for dependency injection. * </p> * * @see org.quartz.Scheduler#setJobFactory(JobFactory) * @see org.quartz.simpl.SimpleJobFactory * @see org.quartz.simpl.PropertySettingJobFactory * * @author James House */ public interface JobFactory { /** * Called by the scheduler at the time of the trigger firing, in order to * produce a <code>Job</code> instance on which to call execute. * * <p> * It should be extremely rare for this method to throw an exception - * basically only the the case where there is no way at all to instantiate * and prepare the Job for execution. When the exception is thrown, the * Scheduler will move all triggers associated with the Job into the * <code>Trigger.STATE_ERROR</code> state, which will require human * intervention (e.g. an application restart after fixing whatever * configuration problem led to the issue wih instantiating the Job. * </p> * * @param bundle * The TriggerFiredBundle from which the <code>JobDetail</code> * and other info relating to the trigger firing can be obtained. * @param scheduler a handle to the scheduler that is about to execute the job. * @throws SchedulerException if there is a problem instantiating the Job. * @return the newly instantiated Job */ Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException; }
重新修改 quartz 配置对象:QuartzConfig
package com.alan.SpringBootQuartz.config; import com.alan.SpringBootQuartz.quartz.QuartzConfigDemo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; /* * Quartz配置类 * @author: Alan_liu * @date 2021/6/5 21:12 */ @Configuration public class QuartzConfig { /*** * 1:创建job对象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public JobDetailFactoryBean jobDetailFactoryBean(){ JobDetailFactoryBean factory=new JobDetailFactoryBean(); //关联自己创建的job类 factory.setJobClass(QuartzConfigDemo.class); return factory; } /*** * 2: 创建trigger对象 * @user Alan_liu * @date 2021/6/5 21:13 */ /* @Bean public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ SimpleTriggerFactoryBean factory =new SimpleTriggerFactoryBean(); //关联jobDetail对象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //该入参表示一个执行的毫秒数 factory.setRepeatInterval(2000); //设置启动后重复执行次数:5次 factory.setRepeatCount(5); return factory; }*/ /** * * Cron Trigger */ @Bean public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){ CronTriggerFactoryBean factory=new CronTriggerFactoryBean(); //关联jobDetail对象 factory.setJobDetail(jobDetailFactoryBean.getObject()); //设置触发时间 factory.setCronExpression("0/2 * * * * ?"); return factory; } /*** * 3:创建Scheduler 对象 * @user Alan_liu * @date 2021/6/5 21:13 */ @Bean public SchedulerFactoryBean schedulerFactoryBean(/*SimpleTriggerFactoryBean simpleTriggerFactoryBean*/CronTriggerFactoryBean cronTriggerFactoryBean,MyAdaptableJobFactory myAdaptableJobFactory ){ SchedulerFactoryBean factory=new SchedulerFactoryBean(); //关联trigger factory.setTriggers(/*simpleTriggerFactoryBean.getObject()*/ cronTriggerFactoryBean.getObject()); //修改该类:完成对象注入spring ioc 容器 factory.setJobFactory(myAdaptableJobFactory); return factory; } }
启动项目:查看执行结果:
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/