一、实现流程:
-
导入依赖:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz --> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency>
-
定时任务-实现Job接口
public class MyJob implements Job{ @Override public void execute(JobExecutionContext context) throws JobExecutionException{ //todo 定时任务代码逻辑 } }
-
定时器工具
public class QuartzJobUtil{ private static SchedulerFactory schedulerFactory = new StdSchedulerFactory(); public static void add(String jobName, String jobGroupName, int time, Class <? extends Job> className){ //创建任务 JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build(); //创建触发器 每 time 秒钟执行一次 Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobName, jobGroupName) .withSchedule(SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(time) .repeatForever()) .build(); try { //获取实例化的 Scheduler。 Scheduler scheduler = schedulerFactory.getScheduler(); //将任务及其触发器放入调度器 scheduler.scheduleJob(jobDetail, trigger); //调度器开始调度任务 if (!scheduler.isShutdown()) { scheduler.start(); //todo 启动任务 } } catch (SchedulerException e) { e.printStackTrace(); } } public void deleteJob(String jobName, String jobGroupName){ try { TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.pauseTrigger(triggerKey);// 停止触发器 scheduler.unscheduleJob(triggerKey);// 移除触发器 scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName)); //todo 删除任务 } catch (SchedulerException e) { e.printStackTrace(); } } }
-
使用定时器工具添加定时任务。
二、需要注意:
-
由于简易,没有持久化操作,重启系统后定时任务丢失。
-
每个定时任务启动后会立即执行一次,需要注意。
延时启动方法:
//QuartzJobUtil.class Trigger trigger = TriggerBuilder.newTrigger() .withIdentity(jobName, jobGroupName) //date为Date类型参数,指到该时间后执行第一次定时任务。 .startAt(date) .withSchedule(SimpleScheduleBuilder .simpleSchedule() .withIntervalInSeconds(time) .withRepeatCount(0)) .build(); //SimpleScheduleBuilder相关API用法 //无限制次数重复定时 SimpleScheduleBuilder.repeatForever() //限制次数重复定时,countNum为定时次数 SimpleScheduleBuilder..withRepeatCount(countNum) //每过time时间执行一次定时任务 SimpleScheduleBuilder.withIntervalInSeconds(time)
-
实际上Quartz有默认的配置文件,你可以使用quartz.properties文件去设置Quartz的参数。默认的配置文件可能并不适用于大量的定时任务,因此需要根据项目需求调整quartz的参数。
-
工具型的写法虽然简单,但是无法使用Spring注入的变量,即使用@Autowired修饰的变量,因为是static修饰,所以需要将Spring注入的变量提前加载好。所以该写法适用于监控任务简单的场景。
建议:使用@Component修饰工具类,不使用static修饰变量与方法,统一使用Spring注入的方式调用工具类。
三、更进一步
-
定时任务持久化
-
添加持久化池依赖:
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
-
编写配置文件quartz.properties。Quartz通过配置文件结合
StdSchedulerFactory
实现载入配置,StdSchedulerFactory
默认加载工作目录下的quartz.properties
文件,读取失败时会尝试加载org/quartz包下的quartz.properties
文件。#quartz.properties # 实例化ThreadPool时,使用的线程类为SimpleThreadPool org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool # threadCount和threadPriority将以setter的形式注入ThreadPool实例 # 并发个数 org.quartz.threadPool.threadCount = 5 # 优先级 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true org.quartz.jobStore.misfireThreshold = 5000 #持久化使用的类 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX #数据库中表的前缀 org.quartz.jobStore.tablePrefix = QRTZ_ #数据源命名 org.quartz.jobStore.dataSource = qzDS #qzDS 数据源 org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver org.quartz.dataSource.qzDS.URL = jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8 org.quartz.dataSource.qzDS.user = root org.quartz.dataSource.qzDS.password = root org.quartz.dataSource.qzDS.maxConnections = 10
-
自定义配置加载,创建
QuartzConfig.java
//QuartzConfig.java import org.quartz.Scheduler; import org.quartz.ee.servlet.QuartzInitializerListener; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import java.io.IOException; import java.util.Properties; /** * Qartz配置, 搭配quartz.properties文件自定义配置, 该处目的使定时任务持久化 */ @Configuration public class QuartzConfig { /** * 读取quartz.properties 文件 * 将值初始化 * @return */ @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } /** * 将配置文件的数据加载到SchedulerFactoryBean中 * @return * @throws IOException */ @Bean public SchedulerFactoryBean schedulerFactoryBean() throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setQuartzProperties(quartzProperties()); return schedulerFactoryBean; } /** * 初始化监听器 * @return */ @Bean public QuartzInitializerListener executorListener(){ return new QuartzInitializerListener(); } /** * 获得Scheduler 对象 * @return * @throws IOException */ @Bean public Scheduler scheduler() throws IOException { return schedulerFactoryBean().getScheduler(); } }
-
数据库创建默认表,从官网或者org/quartz/impl/jdbcjobstore包下找到对应数据库的SQL文件进行创建
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;
-
-
解决直接实现Job接口导致@Autowired修饰的变量为空,并报空指针异常的情况。
原因:Quartz的Job时由自己管理的,因此如果想要在Job中调用Spring管理的Bean,必须让Job也加入Spring容器当中。
-
自定义JobFactory,使用Spring容器管理Quartz的Bean
//参考https://www.cnblogs.com/huahua035/p/7839834.html //MyJobFactory.class 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; /** * Description: 自定义JobFactory,使用Spring容器管理的Quartz的Bean(Job) * <p/> * AdaptableJobFactory是Spring提供的SchedulerFactoryBean的默认实例化工厂,将由直接实例化Job,没有被Spring管理 */ @Component public class MyJobFactory extends AdaptableJobFactory { /** * AutowireCapableBeanFactory接口是BeanFactory的子类 * 可以连接和填充那些生命周期不被Spring管理的已存在的bean实例 * 具体请参考:http://blog.csdn.net/iycynna_123/article/details/52993542 */ @Autowired private AutowireCapableBeanFactory capableBeanFactory; /** * 创建Job实例 * * @param bundle * @return * @throws Exception */ @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { // 实例化对象 Object jobInstance = super.createJobInstance(bundle); // 进行注入(Spring管理该Bean),这是Spring进行注入的API,可以查看Spring官方API文档 capableBeanFactory.autowireBean(jobInstance); //返回对象 return jobInstance; } }
-
在
QuartzConfig.java
配置文件中配置SchedulerFactoryBean//QuartzConfig.java @Autowired private MyJobFactory myJobFactory; @Bean //将一个方法产生为Bean并交给Spring容器管理(@Bean只能用在方法上) public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) throws IOException { //Spring提供SchedulerFactoryBean为Scheduler提供配置信息,并被Spring容器管理其生命周期 SchedulerFactoryBean factory = new SchedulerFactoryBean(); //启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 factory.setOverwriteExistingJobs(true); // 延时启动(秒) factory.setStartupDelay(20); //设置quartz的配置文件 factory.setQuartzProperties(quartzProperties()); //设置自定义Job Factory,用于Spring管理Job bean----此处重点. factory.setJobFactory(myJobFactory); return factory; }
-
编写Job任务
//MyJob.java @Component public class MyJob extends QuartzJobBean { //使用Spring容器中的变量 @Autowired private XxxService xxxService; /** * 执行Job * * @param jobExecutionContext * @throws JobExecutionException */ @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { //todo Job任务逻辑 }
-
其他知识:
-
以Job,Trigger,Scheduler为主要组成部分
-
Trigger:
-
SimpleTrigger:在特定的时间点,或每隔T个时间单位调度一次
-
CronTrigger:基于日历的调度十分有用,比如每个月,每个星期等调度方式
* * * * * * *
cron表达式,从左至右代表秒,分,时,日,月,周,年,一般情况下忽略年
维度,因此6个参数
-
-
如何在Job中增加属性?
- 因为每当scheduler执行Job时,会创建一个Job实例,调用execute()方法后会回收引用,因此无法在Job类中直接定义数据属性。
- 使用JobDataMap,原理跟多线程中使用ThreadLocal类似。
- 在执行Job时,从JobDataMap中取得数据。
- 假如采取了持久化的方式,那么需要注意存储的数据对象的序列化方式。
-
JobFactory:
JobFactory
当trigger触发时,通过Scheduler上配置的JobFactory实例化与之关联的jobs。默认的JobFactory只是在jobs类上调用newInstance()。您可能需要创建自己的JobFactory实现,以完成诸如让应用程序的IoC或DI容器生成/初始化jobs实例之类的操作。
请参阅org.quartz.spi.JobFactory接口以及相关的Scheduler.setJobFactory(fact) 方法。